diff mbox series

correct handling of offsets in bounds warnings (PR 89350)

Message ID 93233447-f886-486e-2167-f029a54ced85@gmail.com
State New
Headers show
Series correct handling of offsets in bounds warnings (PR 89350) | expand

Commit Message

Martin Sebor Feb. 27, 2019, 12:03 a.m. UTC
The false positive in PR89350 is due to -Wstringop-overflow
trusting that the sizetype offset in POINTER_PLUS_EXPR means
the offset is, in fact, unsigned.  Avoiding the false positive
in the cases when this isn't so is trivial but comes at a cost
of false negatives.  Avoiding those will, I expect, require
enhancing the compute_builtin_object_size() function and that
seems risky at this stage so I would like to defer that until
stage 1.  Except in the instance of memset, the false positives
also aren't too serious because the same problem is also
diagnosed by the -Warray-bounds warning in the wrestrict pass.
Unfortunately, the wrestrict pass only handles copy functions
and not memset.

With that as background, the attached patch avoids
the -Wstringop-overflow false positive by disabling the warning
for offsets whose lower bound is positive and upper bound negative.
To avoid the false negatives for memset the patch lets the wrestrict
pass handle the function (for the bounds checking only).  While
testing this I noticed that the wrestrict pass makes the same
assumption about offsets, so it too is susceptible to similar
false positives.  The rest of the patch corrects this problem
n the wrestrict pass.  Because the pass doesn't depend on
the compute_builtin_object_size() function as much as
-Wstringop-overflow, the fix does not cause false positives (at
least none that I came across).

Tested on x86_64-linux.

Martin

Comments

Martin Sebor Feb. 27, 2019, 1:32 a.m. UTC | #1
Please disregard the original patch and consider the attached
version instead.

On 2/26/19 5:03 PM, Martin Sebor wrote:
> The false positive in PR89350 is due to -Wstringop-overflow
> trusting that the sizetype offset in POINTER_PLUS_EXPR means
> the offset is, in fact, unsigned.  Avoiding the false positive
> in the cases when this isn't so is trivial but comes at a cost
> of false negatives.  Avoiding those will, I expect, require
> enhancing the compute_builtin_object_size() function and that
> seems risky at this stage so I would like to defer that until
> stage 1.  Except in the instance of memset, the false positives
> also aren't too serious because the same problem is also
> diagnosed by the -Warray-bounds warning in the wrestrict pass.
> Unfortunately, the wrestrict pass only handles copy functions
> and not memset.
> 
> With that as background, the attached patch avoids
> the -Wstringop-overflow false positive by disabling the warning
> for offsets whose lower bound is positive and upper bound negative.
> To avoid the false negatives for memset the patch lets the wrestrict
> pass handle the function (for the bounds checking only).  While
> testing this I noticed that the wrestrict pass makes the same
> assumption about offsets, so it too is susceptible to similar
> false positives.  The rest of the patch corrects this problem
> n the wrestrict pass.  Because the pass doesn't depend on
> the compute_builtin_object_size() function as much as
> -Wstringop-overflow, the fix does not cause false positives (at
> least none that I came across).
> 
> Tested on x86_64-linux.
> 
> Martin
PR tree-optimization/89350 - Wrong -Wstringop-overflow= warning since r261518

gcc/ChangeLog:

	PR tree-optimization/89350
	* builtins.c (compute_objsize): Also ignore offsets whose upper
	bound is negative.
	* gimple-ssa-warn-restrict.c (builtin_memref): Add new member.
	(builtin_memref::builtin_memref): Initialize new member.
	Allow EXPR to be null.
	(builtin_memref::extend_offset_range): Replace local with a member.
	Avoid assuming pointer offsets are unsigned.
	(builtin_memref::set_base_and_offset): Determine base object
	before computing offset range.
	(builtin_access::builtin_access): Handle memset.
	(builtin_access::generic_overlap): Replace local with a member.
	(builtin_access::strcat_overlap): Same.
	(builtin_access::overlap): Same.
	(maybe_diag_overlap): Same.
	(maybe_diag_access_bounds): Same.
	(wrestrict_dom_walker::check_call): Handle memset.
	(check_bounds_or_overlap): Same.

gcc/testsuite/ChangeLog:

	PR tree-optimization/89350
	* gcc.dg/Wstringop-overflow.c: Xfail overly ambitious tests.
	* gcc.dg/Wstringop-overflow-10.c: New test.
	* gcc.dg/Wstringop-overflow-11.c: New test.
	* gcc.dg/pr89350.c: New test.
	* gcc.dg/pr40340-1.c: Adjust expected warning.
	* gcc.dg/pr40340-2.c: Same.
	* gcc.dg/pr40340-4.c: Same.
	* gcc.dg/pr40340-5.c: Same.

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 269190)
+++ gcc/builtins.c	(working copy)
@@ -3652,7 +3652,8 @@ compute_objsize (tree dest, int ostype)
 		      /* 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))
+		      if (wi::sign_mask (min)
+			  || wi::sign_mask (max))
 			;
 		      else if (wi::ltu_p (min, wisiz))
 			return wide_int_to_tree (TREE_TYPE (size),
Index: gcc/gimple-ssa-warn-restrict.c
===================================================================
--- gcc/gimple-ssa-warn-restrict.c	(revision 269190)
+++ gcc/gimple-ssa-warn-restrict.c	(working copy)
@@ -147,6 +147,9 @@ struct builtin_memref
   /* The size range of the access to this reference.  */
   offset_int sizrange[2];
 
+  /* Cached result of get_max_objsize().  */
+  const offset_int maxobjsize;
+
   /* True for "bounded" string functions like strncat, and strncpy
      and their variants that specify either an exact or upper bound
      on the size of the accesses they perform.  For strncat both
@@ -233,6 +236,7 @@ builtin_memref::builtin_memref (tree expr, tree si
   refoff (HOST_WIDE_INT_MIN),
   offrange (),
   sizrange (),
+  maxobjsize (tree_to_shwi (max_object_size ())),
   strbounded_p ()
 {
   /* Unfortunately, wide_int default ctor is a no-op so array members
@@ -240,7 +244,8 @@ builtin_memref::builtin_memref (tree expr, tree si
   offrange[0] = offrange[1] = 0;
   sizrange[0] = sizrange[1] = 0;
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  if (!expr)
+    return;
 
   /* Find the BASE object or pointer referenced by EXPR and set
      the offset range OFFRANGE in the process.  */
@@ -292,13 +297,13 @@ builtin_memref::builtin_memref (tree expr, tree si
     }
 }
 
-/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.  */
+/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.
+   Pointer offsets are represented as unsigned sizetype but must be
+   treated as signed.  */
 
 void
 builtin_memref::extend_offset_range (tree offset)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
   if (TREE_CODE (offset) == INTEGER_CST)
     {
       offset_int off = int_cst_value (offset);
@@ -312,22 +317,41 @@ builtin_memref::extend_offset_range (tree offset)
 
   if (TREE_CODE (offset) == SSA_NAME)
     {
+      /* A pointer offset is represented as sizetype but treated
+	 as signed.  */
       wide_int min, max;
       value_range_kind rng = get_range_info (offset, &min, &max);
-      if (rng == VR_RANGE)
+      if (rng == VR_ANTI_RANGE && wi::lts_p (max, min))
 	{
+	  /* Convert an anti-range whose upper bound is less than
+	     its lower bound to a signed range.  */
+	  offrange[0] += offset_int::from (max + 1, SIGNED);
+	  offrange[1] += offset_int::from (min - 1, SIGNED);
+	  return;
+	}
+
+      if (rng == VR_RANGE
+	  && (DECL_P (base) || wi::lts_p (min, max)))
+	{
+	  /* Preserve the bounds of the range for an offset into
+	     a known object (it may be adjusted later relative to
+	     a constant offset from its beginning).  Otherwise use
+	     the bounds only when they are ascending when treated
+	     as signed.  */
 	  offrange[0] += offset_int::from (min, SIGNED);
 	  offrange[1] += offset_int::from (max, SIGNED);
+	  return;
 	}
-      else
+
+      /* Handle an anti-range the same as no range at all.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+      tree type;
+      if (is_gimple_assign (stmt)
+	  && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
+	  && INTEGRAL_TYPE_P (type))
 	{
-	  /* Handle an anti-range the same as no range at all.  */
-	  gimple *stmt = SSA_NAME_DEF_STMT (offset);
-	  tree type;
-	  if (is_gimple_assign (stmt)
-	      && gimple_assign_rhs_code (stmt) == NOP_EXPR
-	      && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
-	      && INTEGRAL_TYPE_P (type))
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
 	    {
 	      /* Use the bounds of the type of the NOP_EXPR operand
 		 even if it's signed.  The result doesn't trigger
@@ -334,14 +358,16 @@ builtin_memref::extend_offset_range (tree offset)
 		 warnings but makes their output more readable.  */
 	      offrange[0] += wi::to_offset (TYPE_MIN_VALUE (type));
 	      offrange[1] += wi::to_offset (TYPE_MAX_VALUE (type));
+	      return;
 	    }
-	  else
-	    offrange[1] += maxobjsize;
 	}
-      return;
     }
 
-  offrange[1] += maxobjsize;
+  const offset_int maxoff = tree_to_shwi (max_object_size ()) >> 1;
+  const offset_int minoff = -maxoff - 1;
+
+  offrange[0] += minoff;
+  offrange[1] += maxoff;
 }
 
 /* Determines the base object or pointer of the reference EXPR
@@ -350,7 +376,7 @@ builtin_memref::extend_offset_range (tree offset)
 void
 builtin_memref::set_base_and_offset (tree expr)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  tree offset = NULL_TREE;
 
   if (TREE_CODE (expr) == SSA_NAME)
     {
@@ -377,9 +403,7 @@ builtin_memref::set_base_and_offset (tree expr)
 	  else if (code == POINTER_PLUS_EXPR)
 	    {
 	      expr = gimple_assign_rhs1 (stmt);
-
-	      tree offset = gimple_assign_rhs2 (stmt);
-	      extend_offset_range (offset);
+	      offset = gimple_assign_rhs2 (stmt);
 	    }
 	  else
 	    {
@@ -389,6 +413,12 @@ builtin_memref::set_base_and_offset (tree expr)
 	}
       else
 	{
+	  /* FIXME: Handle PHI nodes in case like:
+	     _12 = &MEM[(void *)&a + 2B] + _10;
+
+	     <bb> [local count: 1073741824]:
+	     # prephitmp_13 = PHI <_12, &MEM[(void *)&a + 2B]>
+	     memcpy (prephitmp_13, p_7(D), 6);  */
 	  base = expr;
 	  return;
 	}
@@ -416,6 +446,9 @@ builtin_memref::set_base_and_offset (tree expr)
   /* get_inner_reference is not expected to return null.  */
   gcc_assert (base != NULL);
 
+  if (offset)
+    extend_offset_range (offset);
+
   poly_int64 bytepos = exact_div (bitpos, BITS_PER_UNIT);
 
   /* Convert the poly_int64 offset to offset_int.  The offset
@@ -471,7 +504,8 @@ builtin_memref::set_base_and_offset (tree expr)
 tree
 builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  if (!ptr)
+    return NULL_TREE;
 
   /* A temporary, possibly adjusted, copy of the offset range.  */
   offset_int offrng[2] = { offrange[0], offrange[1] };
@@ -606,6 +640,14 @@ builtin_access::builtin_access (gimple *call, buil
       detect_overlap = &builtin_access::no_overlap;
       break;
 
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_MEMSET_CHK:
+      /* For memset there is never any overlap to check for.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
     case BUILT_IN_STPNCPY:
     case BUILT_IN_STPNCPY_CHK:
     case BUILT_IN_STRNCPY:
@@ -640,7 +682,7 @@ builtin_access::builtin_access (gimple *call, buil
       return;
     }
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dst.maxobjsize;
 
   /* Try to determine the size of the base object.  compute_objsize
      expects a pointer so create one if BASE is a non-pointer object.  */
@@ -659,7 +701,7 @@ builtin_access::builtin_access (gimple *call, buil
 	dst.basesize = maxobjsize;
     }
 
-  if (src.basesize < 0)
+  if (src.base && src.basesize < 0)
     {
       addr = src.base;
       if (!POINTER_TYPE_P (TREE_TYPE (addr)))
@@ -845,7 +887,7 @@ builtin_access::generic_overlap ()
 
   gcc_assert (dstref->base == srcref->base);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = acs.dstref->maxobjsize;
 
   offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
   gcc_assert (maxsize <= maxobjsize);
@@ -1054,7 +1096,7 @@ builtin_access::strcat_overlap ()
 
   gcc_assert (dstref->base == srcref->base);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = acs.dstref->maxobjsize;
 
   gcc_assert (dstref->base && dstref->base == srcref->base);
 
@@ -1191,7 +1233,7 @@ builtin_access::overlap ()
 {
   builtin_access &acs = *this;
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dstref->maxobjsize;
 
   acs.sizrange[0] = wi::smax (dstref->sizrange[0],
 			      srcref->sizrange[0]).to_shwi ();
@@ -1372,7 +1414,7 @@ maybe_diag_overlap (location_t loc, gimple *call,
 	     "[" HOST_WIDE_INT_PRINT_DEC ", " HOST_WIDE_INT_PRINT_DEC "]",
 	     ovloff[0], ovloff[1]);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dstref.maxobjsize;
   bool must_overlap = ovlsiz[0] > 0;
 
   if (ovlsiz[1] == 0)
@@ -1581,7 +1623,7 @@ static bool
 maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict,
 			  const builtin_memref &ref, bool do_warn)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = ref.maxobjsize;
 
   /* Check for excessive size first and regardless of warning options
      since the result is used to make codegen decisions.  */
@@ -1690,8 +1732,6 @@ maybe_diag_access_bounds (location_t loc, gimple *
     }
   else if (oobref == ref.base)
     {
-      const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
       /* True when the offset formed by an access to the reference
 	 is out of bounds, rather than the initial offset wich is
 	 in bounds.  This implies access past the end.  */
@@ -1814,6 +1854,12 @@ wrestrict_dom_walker::check_call (gimple *call)
       bnd_idx = 2;
       break;
 
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_MEMSET_CHK:
+      dst_idx = 0;
+      bnd_idx = 2;
+      break;
+
     case BUILT_IN_STPCPY:
     case BUILT_IN_STPCPY_CHK:
     case BUILT_IN_STRCPY:
@@ -1844,7 +1890,7 @@ wrestrict_dom_walker::check_call (gimple *call)
 
   /* DST and SRC can be null for a call with an insufficient number
      of arguments to a built-in function declared without a protype.  */
-  if (!dst || !src)
+  if (!dst || (src_idx < nargs && !src))
     return;
 
   /* DST, SRC, or DSTWR can also have the wrong type in a call to
@@ -1851,7 +1897,7 @@ wrestrict_dom_walker::check_call (gimple *call)
      a function declared without a prototype.  Avoid checking such
      invalid calls.  */
   if (TREE_CODE (TREE_TYPE (dst)) != POINTER_TYPE
-      || TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE
+      || (src && TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE)
       || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
@@ -1901,15 +1947,23 @@ check_bounds_or_overlap (gimple *call, tree dst, t
       return OPT_Warray_bounds;
     }
 
-  bool check_overlap
-    = (warn_restrict
-       && (bounds_only
-	   || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
-	       && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
-
-  if (!check_overlap)
+  if (!warn_restrict || bounds_only || !src)
     return 0;
 
+  if (!bounds_only)
+    {
+      switch (DECL_FUNCTION_CODE (func))
+	{
+	case BUILT_IN_MEMMOVE:
+	case BUILT_IN_MEMMOVE_CHK:
+	case BUILT_IN_MEMSET:
+	case BUILT_IN_MEMSET_CHK:
+	  return 0;
+	default:
+	  break;
+	}
+    }
+
   if (operand_equal_p (dst, src, 0))
     {
       /* Issue -Wrestrict unless the pointers are null (those do
Index: gcc/testsuite/gcc.dg/Wstringop-overflow.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow.c	(working copy)
@@ -51,8 +51,8 @@ void test_memcpy_array (const void *s)
   T (a7 + UR (8, 9), s, 7);   /* { dg-warning "writing 7 bytes into a region of size 0" } */
 
   T (a7 + UR (9, 10), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, SIZE_MAX), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" } */
+  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
+  T (a7 + UR (DIFF_MAX, SIZE_MAX), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-*} } */
 
   /* This is valid.  */
   char *d = a7 + 7;
@@ -102,8 +102,8 @@ void test_strcpy_array (void)
   T (a7 + UR (8, 9), "012345");   /* { dg-warning "writing 7 bytes into a region of size 0" } */
 
   T (a7 + UR (9, 10), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, SIZE_MAX), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" } */
+  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
+  T (a7 + UR (DIFF_MAX, SIZE_MAX), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
 
   char *d = a7 + 7;
 
@@ -127,6 +127,6 @@ void test_strncpy_memarray (struct MemArray *p, co
   T (p->a9 + UR (9, 10), s, 9);   /* { dg-warning "writing 9 bytes into a region of size 0" } */
   T (p->a9 + UR (10, 11), s, 9);  /* { dg-warning "writing 9 bytes into a region of size 0" } */
 
-  T (p->a9 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 1);  /* { dg-warning "writing 1 byte into a region of size 0" } */
-  T (p->a9 + UR (DIFF_MAX, SIZE_MAX), s, 3);  /* { dg-warning "writing 3 bytes into a region of size 0" } */
+  T (p->a9 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 1);  /* { dg-warning "writing 1 byte into a region of size 0" "pr85350" { xfail *-*-* } } */
+  T (p->a9 + UR (DIFF_MAX, SIZE_MAX), s, 3);  /* { dg-warning "writing 3 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
 }
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-10.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-10.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-10.c	(working copy)
@@ -0,0 +1,120 @@
+/* PR tree-optimization/89350 - Wrong -Wstringop-overflow warning
+   on a variable offset from the end of an array
+   Test exercising -Wstringop-truncation alone, with -Warray-bounds
+   explicitly disabled.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds -Wstringop-overflow -ftrack-macro-expansion=0" }  */
+
+#include "range.h"
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+
+void sink (void*);
+
+extern char ga7[7];
+
+
+#define T(d, n) (memcpy ((d), s, (n)), sink (d))
+
+void test_memcpy_array_cst_range_off (const void *s)
+{
+  char *d = ga7 + 1;
+
+  T (d + UR (1, 2), 1);
+  T (d + UR (1, 2), 5);
+
+  T (d + UR (0, 1), 6);
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  T (d + UR (1, 2), 6);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
+  T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
+
+  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 1);
+  T (d + SR (-2, -1), 2);     /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+
+  d = ga7 + 7;
+  T (d + SR (-7, -6), 1);
+  T (d + SR (-7, -1), 1);
+  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+
+  T (d + UR (1, 2), 1);       /* { dg-warning "writing 1 byte into a region of size 0 " "pr89350" { xfail *-*-* } } */
+}
+
+
+void test_memcpy_array_range_range_off (const void *s)
+{
+  char *d = ga7 + UR (0, 1);
+  T (d + SR (-1, 0), 1);
+  T (d + SR (-1, 0), 7);
+  T (d + SR (-1, 0), 9);       /* { dg-warning "writing 1 byte into a region of size 0 " "pr89350" { xfail *-*-* } } */
+}
+
+
+#undef T
+#define T(d, n) (memset ((d), 0, (n)), sink (d))
+
+void test_memset_array_unsigned_off (void)
+{
+  char *d = ga7 + 1;
+
+  T (d + UR (1, 2), 1);
+  T (d + UR (1, 2), 5);
+
+  T (d + UR (0, 1), 6);
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  T (d + UR (1, 2), 6);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */
+  T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
+
+  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 1);
+  T (d + SR (-2, -1), 2);     /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+
+  d = ga7 + 7;
+  T (d + SR (-7, -6), 1);
+  T (d + SR (-7, -1), 1);
+  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+
+  T (d + UR (1, 2), 1);       /* { dg-warning "writing 1 byte into a region of size 0 " } */
+}
+
+
+
+struct MemArray { char a7[7], a3[3], c; };
+
+extern struct MemArray gma;
+
+void test_memset_memarray (void)
+{
+  char *d = gma.a7 + 1;
+
+  T (d + UR (1, 2), 1);
+  T (d + UR (1, 2), 5);
+
+  T (d + UR (0, 1), 6);
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  T (d + UR (1, 2), 6);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89350" { xfail *-*-* } } */
+  T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " "pr85350" { xfail *-*-* } } */
+
+}
+
+
+#undef T
+#define T(d, n) (memcpy ((d), s, (n)), sink (d))
+
+void test_memcpy_array_signed_off (const void *s)
+{
+  char *d = ga7 + 1;
+
+  T (d + SR (-7, 7), 7);
+  T (d + SR (-1, 1), 7);
+  T (d + SR (-1, 1), 9);      /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-1, 2), 9);      /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
+  T (d + SR (1, 2), 1);
+  T (d + SR (1, 2), 5);
+
+  T (d + SR (0, 1), 6);
+  T (d + UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-11.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-11.c	(working copy)
@@ -0,0 +1,321 @@
+/* PR tree-optimization/89350 - Wrong -Wstringop-overflow warning
+   on a variable offset from the end of an array
+   Test exercising -Wstringop-truncation with -Wall.
+   -Wstringop-truncation is disabled to avoid warnings for strncpy
+   calls whose bound matches the size of the destination getting
+   in the way of -Wstringop-overflow.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-stringop-truncation -ftrack-macro-expansion=0" }  */
+
+#include "range.h"
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+extern char* strcpy (char*, const char*);
+extern char* strncpy (char*, const char*, size_t);
+
+void sink (void*);
+
+#define CAT(pfx, line) pfx ## line
+#define CONCAT(pfx, line) CAT (pfx, line)
+#define UNIQ_NAME(pfx) CONCAT (pfx, __LINE__)
+
+/* Exercise a call to memset with a distinct destination object each
+   time to prevent GCC from reusing the destination pointer in later
+   tests.  */
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    memset (d, 0, n);				\
+    sink (d);					\
+  } while (0)
+
+
+/* Exercise calls to memset with a destination pointer pointing to
+   an array plus constant offset plus variable offset, in that order.  */
+
+void test_memset_array_cst_range_off (void)
+{
+  T (1, SR (-7, 7), 7);
+  T (1, SR (-1, 1), 7);
+  T (1, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 1);
+  T (1, SR ( 1, 2), 5);
+
+  T (1, SR ( 0, 1), 6);
+  T (1, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR (-7, 7), 7);
+  T (2, SR (-2, 7), 7);
+  T (2, SR (-1, 1), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 1);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);
+  T (2, SR ( 1, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 7);
+  T (7, UR (-7, 0), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 3);
+  T (7, UR (-2, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+/* Exercise calls to memset with a destination pointer pointing to
+   an array plus variable offset plus constant offset.  */
+
+void test_memset_array_range_cst_off (void)
+{
+  T (SR (-7, 7), 1, 7);
+  T (SR (-1, 1), 1, 7);
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 5);
+
+  T (SR ( 0, 1), 1, 6);
+  T (UR ( 1, 2), 1, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 7);
+  T (SR (-1, 1), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);
+  T (SR ( 1, 2), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_memset_array_range_range_off (void)
+{
+  T (UR (0, 1), UR (0, 1), 7);
+  T (UR (3, 5), UR (2, 7), 1);
+  T (UR (3, 7), UR (2, 9), 2);
+  T (UR (3, 9), UR (2, 9), 3);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (UR (0, 1), UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+#undef T
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    memcpy (d, s, n);				\
+    sink (d);					\
+  } while (0)
+
+
+void test_memcpy_array_cst_range_off (const void *s)
+{
+  T (1, SR (-7, 7), 7);
+  T (1, SR (-1, 1), 7);
+  T (1, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 1);
+  T (1, SR ( 1, 2), 5);
+
+  T (1, SR ( 0, 1), 6);
+  T (1, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR (-7, 7), 7);
+  T (2, SR (-2, 7), 7);
+  T (2, SR (-1, 1), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 1);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);
+  T (2, SR ( 1, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 7);
+  T (7, UR (-7, 0), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 3);
+  T (7, UR (-2, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_memcpy_array_range_cst_off (const void *s)
+{
+  T (SR (-7, 7), 1, 7);
+  T (SR (-1, 1), 1, 7);
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 5);
+
+  T (SR ( 0, 1), 1, 6);
+  T (UR ( 1, 2), 1, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 7);
+  T (SR (-1, 1), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);
+  T (SR ( 1, 2), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_memcpy_array_range_range_off (const void *s)
+{
+  T (UR (0, 1), UR (0, 1), 7);
+  T (UR (3, 5), UR (2, 7), 1);
+  T (UR (3, 7), UR (2, 9), 2);
+  T (UR (3, 9), UR (2, 9), 3);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (UR (0, 1), UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+#undef T
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    const char str[] = "0123456789";		\
+    const char *s = str + sizeof str - 1 - n;   \
+    strcpy (d, s);				\
+    sink (d);					\
+  } while (0)
+
+
+void test_strcpy_array_cst_range_off (void)
+{
+  T (1, SR (-7, 7), 6);
+  T (1, SR (-1, 1), 6);
+  T (1, SR (-1, 1), 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 0);
+  T (1, SR ( 1, 2), 4);
+
+  T (1, SR ( 0, 1), 5);
+  T (1, UR ( 1, 2), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR (-7, 7), 6);
+  T (2, SR (-2, 7), 6);
+  T (2, SR (-1, 1), 6);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR (-1, 1), 8);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 0);
+  T (2, SR ( 1, 2), 2);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 6);
+  T (7, UR (-7, 0), 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 2);
+  T (7, UR (-2, 2), 4);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_strcpy_array_range_cst_off (const char *s)
+{
+  T (SR (-7, 7), 1, 6);
+  T (SR (-1, 1), 1, 6);
+  T (SR (-1, 1), 1, 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 0);
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 4);
+
+  T (SR ( 0, 1), 1, 5);
+  T (UR ( 1, 2), 1, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 6);
+  T (SR (-1, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 0);
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 2);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+#undef T
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    strncpy (d, s, n);				\
+    sink (d);					\
+  } while (0)
+
+
+void test_strncpy_array_cst_range_off (const char *s)
+{
+  T (1, SR (-7, 7), 7);
+  T (1, SR (-1, 1), 7);
+  T (1, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 1);
+  T (1, SR ( 1, 2), 5);
+
+  T (1, SR ( 0, 1), 6);
+  T (1, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( -7, 7), 7);
+  T (2, SR ( -1, 1), 7);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( -1, 1), 9);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 1);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);
+  T (2, SR ( 1, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 7);
+  T (7, UR (-7, 0), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 3);
+  T (7, UR (-2, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_strncpy_array_range_cst_off (const char *s)
+{
+  T (SR (-7, 7), 1, 7);
+  T (SR (-1, 1), 1, 7);
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 5);
+
+  T (SR ( 0, 1), 1, 6);
+  T (UR ( 1, 2), 1, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 7);
+  T (SR (-1, 1), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);
+  T (SR ( 1, 2), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_strncpy_array_range_range_off (const char *s)
+{
+  T (UR (0, 1), UR (0, 1), 7);
+  T (UR (3, 5), UR (2, 7), 1);
+  T (UR (3, 7), UR (2, 9), 2);
+  T (UR (3, 9), UR (2, 9), 3);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (UR (0, 1), UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
Index: gcc/testsuite/gcc.dg/pr40340-1.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-1.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-1.c	(working copy)
@@ -20,5 +20,5 @@ main (void)
   return 0;
 }
 
-/* { dg-warning "writing" "" { target *-*-* } 10 } */
+/* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
Index: gcc/testsuite/gcc.dg/pr40340-2.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-2.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-2.c	(working copy)
@@ -12,5 +12,5 @@ main (void)
   return 0;
 }
 
-/* { dg-warning "writing" "" { target *-*-* } 10 } */
+/* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
Index: gcc/testsuite/gcc.dg/pr40340-4.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-4.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-4.c	(working copy)
@@ -1,6 +1,6 @@
 /* PR middle-end/40340 */
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall -Wno-system-headers -g" } */
+/* { dg-options "-O2 -Wall -Wno-array-bounds -Wno-system-headers -g" } */
 
 #define TEST3
 #include "pr40340.h"
Index: gcc/testsuite/gcc.dg/pr40340-5.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-5.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-5.c	(working copy)
@@ -13,5 +13,5 @@ main (void)
   return 0;
 }
 
-/* { dg-warning "writing" "" { target *-*-* } 10 } */
+/* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
Index: gcc/testsuite/gcc.dg/pr89350.c
===================================================================
--- gcc/testsuite/gcc.dg/pr89350.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/pr89350.c	(working copy)
@@ -0,0 +1,18 @@
+/* PR tree-optimization/89350 - Wrong -Wstringop-overflow warning
+   on a variable offset from the end of an array
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+char buf[128];
+char *src = "HCSparta";
+
+int main(int argc, char **argv)
+{
+    char *dst = buf + sizeof(buf);
+
+    if (argc)
+    {
+      dst -= argc;
+      __builtin_memcpy(dst, src, argc + 0);   /* { dg-bogus "\\\[-Warray-bounds|-Wstringop-overflow" } */
+    }
+}
Martin Sebor March 6, 2019, 9:40 p.m. UTC | #2
Ping: https://gcc.gnu.org/ml/gcc-patches/2019-02/msg02029.html

(This is marked as a P1 regression.)

On 2/26/19 6:32 PM, Martin Sebor wrote:
> Please disregard the original patch and consider the attached
> version instead.
> 
> On 2/26/19 5:03 PM, Martin Sebor wrote:
>> The false positive in PR89350 is due to -Wstringop-overflow
>> trusting that the sizetype offset in POINTER_PLUS_EXPR means
>> the offset is, in fact, unsigned.  Avoiding the false positive
>> in the cases when this isn't so is trivial but comes at a cost
>> of false negatives.  Avoiding those will, I expect, require
>> enhancing the compute_builtin_object_size() function and that
>> seems risky at this stage so I would like to defer that until
>> stage 1.  Except in the instance of memset, the false positives
>> also aren't too serious because the same problem is also
>> diagnosed by the -Warray-bounds warning in the wrestrict pass.
>> Unfortunately, the wrestrict pass only handles copy functions
>> and not memset.
>>
>> With that as background, the attached patch avoids
>> the -Wstringop-overflow false positive by disabling the warning
>> for offsets whose lower bound is positive and upper bound negative.
>> To avoid the false negatives for memset the patch lets the wrestrict
>> pass handle the function (for the bounds checking only).  While
>> testing this I noticed that the wrestrict pass makes the same
>> assumption about offsets, so it too is susceptible to similar
>> false positives.  The rest of the patch corrects this problem
>> n the wrestrict pass.  Because the pass doesn't depend on
>> the compute_builtin_object_size() function as much as
>> -Wstringop-overflow, the fix does not cause false positives (at
>> least none that I came across).
>>
>> Tested on x86_64-linux.
>>
>> Martin
>
Martin Sebor March 12, 2019, 2:52 a.m. UTC | #3
Ping: https://gcc.gnu.org/ml/gcc-patches/2019-02/msg02029.html

On 3/6/19 2:40 PM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-02/msg02029.html
> 
> (This is marked as a P1 regression.)
> 
> On 2/26/19 6:32 PM, Martin Sebor wrote:
>> Please disregard the original patch and consider the attached
>> version instead.
>>
>> On 2/26/19 5:03 PM, Martin Sebor wrote:
>>> The false positive in PR89350 is due to -Wstringop-overflow
>>> trusting that the sizetype offset in POINTER_PLUS_EXPR means
>>> the offset is, in fact, unsigned.  Avoiding the false positive
>>> in the cases when this isn't so is trivial but comes at a cost
>>> of false negatives.  Avoiding those will, I expect, require
>>> enhancing the compute_builtin_object_size() function and that
>>> seems risky at this stage so I would like to defer that until
>>> stage 1.  Except in the instance of memset, the false positives
>>> also aren't too serious because the same problem is also
>>> diagnosed by the -Warray-bounds warning in the wrestrict pass.
>>> Unfortunately, the wrestrict pass only handles copy functions
>>> and not memset.
>>>
>>> With that as background, the attached patch avoids
>>> the -Wstringop-overflow false positive by disabling the warning
>>> for offsets whose lower bound is positive and upper bound negative.
>>> To avoid the false negatives for memset the patch lets the wrestrict
>>> pass handle the function (for the bounds checking only).  While
>>> testing this I noticed that the wrestrict pass makes the same
>>> assumption about offsets, so it too is susceptible to similar
>>> false positives.  The rest of the patch corrects this problem
>>> n the wrestrict pass.  Because the pass doesn't depend on
>>> the compute_builtin_object_size() function as much as
>>> -Wstringop-overflow, the fix does not cause false positives (at
>>> least none that I came across).
>>>
>>> Tested on x86_64-linux.
>>>
>>> Martin
>>
>
Martin Sebor March 18, 2019, 4:53 p.m. UTC | #4
Ping: https://gcc.gnu.org/ml/gcc-patches/2019-02/msg02029.html

As an aside, I introduced the same mistake/false positive in
three places: -Wstringop-overflow in builtins.c, -Warray-bounds
in gimple-ssa-warn-restrict.c, and -Warray-bounds in tree-vrp.c.
This patch fixes the first two.  I'm working on PR 89720 to fix
the last one.

On 3/11/19 8:52 PM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-02/msg02029.html
> 
> On 3/6/19 2:40 PM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-02/msg02029.html
>>
>> (This is marked as a P1 regression.)
>>
>> On 2/26/19 6:32 PM, Martin Sebor wrote:
>>> Please disregard the original patch and consider the attached
>>> version instead.
>>>
>>> On 2/26/19 5:03 PM, Martin Sebor wrote:
>>>> The false positive in PR89350 is due to -Wstringop-overflow
>>>> trusting that the sizetype offset in POINTER_PLUS_EXPR means
>>>> the offset is, in fact, unsigned.  Avoiding the false positive
>>>> in the cases when this isn't so is trivial but comes at a cost
>>>> of false negatives.  Avoiding those will, I expect, require
>>>> enhancing the compute_builtin_object_size() function and that
>>>> seems risky at this stage so I would like to defer that until
>>>> stage 1.  Except in the instance of memset, the false positives
>>>> also aren't too serious because the same problem is also
>>>> diagnosed by the -Warray-bounds warning in the wrestrict pass.
>>>> Unfortunately, the wrestrict pass only handles copy functions
>>>> and not memset.
>>>>
>>>> With that as background, the attached patch avoids
>>>> the -Wstringop-overflow false positive by disabling the warning
>>>> for offsets whose lower bound is positive and upper bound negative.
>>>> To avoid the false negatives for memset the patch lets the wrestrict
>>>> pass handle the function (for the bounds checking only).  While
>>>> testing this I noticed that the wrestrict pass makes the same
>>>> assumption about offsets, so it too is susceptible to similar
>>>> false positives.  The rest of the patch corrects this problem
>>>> n the wrestrict pass.  Because the pass doesn't depend on
>>>> the compute_builtin_object_size() function as much as
>>>> -Wstringop-overflow, the fix does not cause false positives (at
>>>> least none that I came across).
>>>>
>>>> Tested on x86_64-linux.
>>>>
>>>> Martin
>>>
>>
>
Jeff Law March 20, 2019, 7:04 p.m. UTC | #5
On 2/26/19 6:32 PM, Martin Sebor wrote:
> Please disregard the original patch and consider the attached
> version instead.
> 
> On 2/26/19 5:03 PM, Martin Sebor wrote:
>> The false positive in PR89350 is due to -Wstringop-overflow
>> trusting that the sizetype offset in POINTER_PLUS_EXPR means
>> the offset is, in fact, unsigned.  Avoiding the false positive
>> in the cases when this isn't so is trivial but comes at a cost
>> of false negatives.  Avoiding those will, I expect, require
>> enhancing the compute_builtin_object_size() function and that
>> seems risky at this stage so I would like to defer that until
>> stage 1.  Except in the instance of memset, the false positives
>> also aren't too serious because the same problem is also
>> diagnosed by the -Warray-bounds warning in the wrestrict pass.
>> Unfortunately, the wrestrict pass only handles copy functions
>> and not memset.
>>
>> With that as background, the attached patch avoids
>> the -Wstringop-overflow false positive by disabling the warning
>> for offsets whose lower bound is positive and upper bound negative.
>> To avoid the false negatives for memset the patch lets the wrestrict
>> pass handle the function (for the bounds checking only).  While
>> testing this I noticed that the wrestrict pass makes the same
>> assumption about offsets, so it too is susceptible to similar
>> false positives.  The rest of the patch corrects this problem
>> n the wrestrict pass.  Because the pass doesn't depend on
>> the compute_builtin_object_size() function as much as
>> -Wstringop-overflow, the fix does not cause false positives (at
>> least none that I came across).
>>
>> Tested on x86_64-linux.
>>
>> Martin
> 
> 
> gcc-89350.diff
> 
> PR tree-optimization/89350 - Wrong -Wstringop-overflow= warning since r261518
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/89350
> 	* builtins.c (compute_objsize): Also ignore offsets whose upper
> 	bound is negative.
> 	* gimple-ssa-warn-restrict.c (builtin_memref): Add new member.
> 	(builtin_memref::builtin_memref): Initialize new member.
> 	Allow EXPR to be null.
> 	(builtin_memref::extend_offset_range): Replace local with a member.
> 	Avoid assuming pointer offsets are unsigned.
> 	(builtin_memref::set_base_and_offset): Determine base object
> 	before computing offset range.
> 	(builtin_access::builtin_access): Handle memset.
> 	(builtin_access::generic_overlap): Replace local with a member.
> 	(builtin_access::strcat_overlap): Same.
> 	(builtin_access::overlap): Same.
> 	(maybe_diag_overlap): Same.
> 	(maybe_diag_access_bounds): Same.
> 	(wrestrict_dom_walker::check_call): Handle memset.
> 	(check_bounds_or_overlap): Same.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/89350
> 	* gcc.dg/Wstringop-overflow.c: Xfail overly ambitious tests.
> 	* gcc.dg/Wstringop-overflow-10.c: New test.
> 	* gcc.dg/Wstringop-overflow-11.c: New test.
> 	* gcc.dg/pr89350.c: New test.
> 	* gcc.dg/pr40340-1.c: Adjust expected warning.
> 	* gcc.dg/pr40340-2.c: Same.
> 	* gcc.dg/pr40340-4.c: Same.
> 	* gcc.dg/pr40340-5.c: Same.
OK.  And just to be clear, totally agree with not trying to change
c_b_o_s to return a range at this point in the release cycle.

jeff
diff mbox series

Patch

PR tree-optimization/89350 - Wrong -Wstringop-overflow= warning since r261518

gcc/ChangeLog:

	PR tree-optimization/89350
	* builtins.c (compute_objsize): Also ignore offsets whose upper
	bound is negative.
	* gimple-ssa-warn-restrict.c (builtin_memref): Add new member.
	(builtin_memref::builtin_memref): Initialize new member.
	Allow EXPR to be null.
	(builtin_memref::extend_offset_range): Replace local with a member.
	Avoid assuming pointer offsets are unsigned.
	(builtin_memref::set_base_and_offset): Determine base object
	before computing offset range.
	(builtin_access::builtin_access): Handle memset.
	(builtin_access::generic_overlap): Replace local with a member.
	(builtin_access::strcat_overlap): Same.
	(builtin_access::overlap): Same.
	(maybe_diag_overlap): Same.
	(maybe_diag_access_bounds): Same.
	(wrestrict_dom_walker::check_call): Handle memset.
	(check_bounds_or_overlap): Same.

gcc/testsuite/ChangeLog:

	PR tree-optimization/89350
	* gcc.dg/Wstringop-overflow.c: Xfail overly ambitious tests.
	* gcc.dg/Wstringop-overflow-10.c: New test.
	* gcc.dg/Wstringop-overflow-11.c: New test.
	* gcc.dg/pr40340-1.c: Adjust expected warning.
	* gcc.dg/pr40340-2.c: Same.
	* gcc.dg/pr40340-4.c: Same.
	* gcc.dg/pr40340-5.c: Same.

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 269190)
+++ gcc/builtins.c	(working copy)
@@ -3652,7 +3652,8 @@  compute_objsize (tree dest, int ostype)
 		      /* 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))
+		      if (wi::sign_mask (min)
+			  || wi::sign_mask (max))
 			;
 		      else if (wi::ltu_p (min, wisiz))
 			return wide_int_to_tree (TREE_TYPE (size),
Index: gcc/gimple-ssa-warn-restrict.c
===================================================================
--- gcc/gimple-ssa-warn-restrict.c	(revision 269190)
+++ gcc/gimple-ssa-warn-restrict.c	(working copy)
@@ -147,6 +147,9 @@  struct builtin_memref
   /* The size range of the access to this reference.  */
   offset_int sizrange[2];
 
+  /* Cached result of get_max_objsize().  */
+  const offset_int maxobjsize;
+
   /* True for "bounded" string functions like strncat, and strncpy
      and their variants that specify either an exact or upper bound
      on the size of the accesses they perform.  For strncat both
@@ -233,6 +236,7 @@  builtin_memref::builtin_memref (tree expr, tree si
   refoff (HOST_WIDE_INT_MIN),
   offrange (),
   sizrange (),
+  maxobjsize (tree_to_shwi (max_object_size ())),
   strbounded_p ()
 {
   /* Unfortunately, wide_int default ctor is a no-op so array members
@@ -240,7 +244,8 @@  builtin_memref::builtin_memref (tree expr, tree si
   offrange[0] = offrange[1] = 0;
   sizrange[0] = sizrange[1] = 0;
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  if (!expr)
+    return;
 
   /* Find the BASE object or pointer referenced by EXPR and set
      the offset range OFFRANGE in the process.  */
@@ -292,13 +297,13 @@  builtin_memref::builtin_memref (tree expr, tree si
     }
 }
 
-/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.  */
+/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.
+   Pointer offsets are represented as unsigned sizetype but must be
+   treated as signed.  */
 
 void
 builtin_memref::extend_offset_range (tree offset)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
   if (TREE_CODE (offset) == INTEGER_CST)
     {
       offset_int off = int_cst_value (offset);
@@ -310,24 +315,41 @@  builtin_memref::extend_offset_range (tree offset)
       return;
     }
 
+  const offset_int maxoff = tree_to_shwi (max_object_size ()) >> 1;
+  offset_int minoff = -maxoff - 1;
+
   if (TREE_CODE (offset) == SSA_NAME)
     {
+      /* A pointer offset is represented as sizetype but treated
+	 as signed.  */
       wide_int min, max;
       value_range_kind rng = get_range_info (offset, &min, &max);
-      if (rng == VR_RANGE)
+      if (rng == VR_ANTI_RANGE && wi::lts_p (max, min))
 	{
+	  /* Convert an anti-range whose upper bound is less than
+	     its lower bound to a signed range.  */
+	  offrange[0] += offset_int::from (max + 1, SIGNED);
+	  offrange[1] += offset_int::from (min - 1, SIGNED);
+	  return;
+	}
+
+      if (rng == VR_RANGE && wi::lts_p (min, max))
+	{
 	  offrange[0] += offset_int::from (min, SIGNED);
 	  offrange[1] += offset_int::from (max, SIGNED);
+	  return;
 	}
-      else
+
+      /* Handle an anti-range the same as no range at all.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+      tree type;
+      if (is_gimple_assign (stmt)
+	  && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
+	  && INTEGRAL_TYPE_P (type))
 	{
-	  /* Handle an anti-range the same as no range at all.  */
-	  gimple *stmt = SSA_NAME_DEF_STMT (offset);
-	  tree type;
-	  if (is_gimple_assign (stmt)
-	      && gimple_assign_rhs_code (stmt) == NOP_EXPR
-	      && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
-	      && INTEGRAL_TYPE_P (type))
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NEGATE_EXPR
+	      || code == NOP_EXPR)
 	    {
 	      /* Use the bounds of the type of the NOP_EXPR operand
 		 even if it's signed.  The result doesn't trigger
@@ -334,14 +356,20 @@  builtin_memref::extend_offset_range (tree offset)
 		 warnings but makes their output more readable.  */
 	      offrange[0] += wi::to_offset (TYPE_MIN_VALUE (type));
 	      offrange[1] += wi::to_offset (TYPE_MAX_VALUE (type));
+	      return;
 	    }
-	  else
-	    offrange[1] += maxobjsize;
 	}
-      return;
+
+      /* Preserve the lower bound of the range for an offset into
+	 a known object (it may be adjusted later relative to
+	 a constant offset from its beginning).  Otherwise use
+	 the minum possible value.  */
+      if (DECL_P (base) && rng == VR_RANGE)
+	minoff = offset_int::from (min, SIGNED);
     }
 
-  offrange[1] += maxobjsize;
+  offrange[0] += minoff;
+  offrange[1] += maxoff;
 }
 
 /* Determines the base object or pointer of the reference EXPR
@@ -350,7 +378,7 @@  builtin_memref::extend_offset_range (tree offset)
 void
 builtin_memref::set_base_and_offset (tree expr)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  tree offset = NULL_TREE;
 
   if (TREE_CODE (expr) == SSA_NAME)
     {
@@ -377,9 +405,7 @@  builtin_memref::set_base_and_offset (tree expr)
 	  else if (code == POINTER_PLUS_EXPR)
 	    {
 	      expr = gimple_assign_rhs1 (stmt);
-
-	      tree offset = gimple_assign_rhs2 (stmt);
-	      extend_offset_range (offset);
+	      offset = gimple_assign_rhs2 (stmt);
 	    }
 	  else
 	    {
@@ -389,6 +415,12 @@  builtin_memref::set_base_and_offset (tree expr)
 	}
       else
 	{
+	  /* FIXME: Handle PHI nodes in case like:
+	     _12 = &MEM[(void *)&a + 2B] + _10;
+
+	     <bb> [local count: 1073741824]:
+	     # prephitmp_13 = PHI <_12, &MEM[(void *)&a + 2B]>
+	     memcpy (prephitmp_13, p_7(D), 6);  */
 	  base = expr;
 	  return;
 	}
@@ -416,6 +448,9 @@  builtin_memref::set_base_and_offset (tree expr)
   /* get_inner_reference is not expected to return null.  */
   gcc_assert (base != NULL);
 
+  if (offset)
+    extend_offset_range (offset);
+
   poly_int64 bytepos = exact_div (bitpos, BITS_PER_UNIT);
 
   /* Convert the poly_int64 offset to offset_int.  The offset
@@ -471,7 +506,8 @@  builtin_memref::set_base_and_offset (tree expr)
 tree
 builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  if (!ptr)
+    return NULL_TREE;
 
   /* A temporary, possibly adjusted, copy of the offset range.  */
   offset_int offrng[2] = { offrange[0], offrange[1] };
@@ -606,6 +642,14 @@  builtin_access::builtin_access (gimple *call, buil
       detect_overlap = &builtin_access::no_overlap;
       break;
 
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_MEMSET_CHK:
+      /* For memset there is never any overlap to check for.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
     case BUILT_IN_STPNCPY:
     case BUILT_IN_STPNCPY_CHK:
     case BUILT_IN_STRNCPY:
@@ -640,7 +684,7 @@  builtin_access::builtin_access (gimple *call, buil
       return;
     }
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dst.maxobjsize;
 
   /* Try to determine the size of the base object.  compute_objsize
      expects a pointer so create one if BASE is a non-pointer object.  */
@@ -659,7 +703,7 @@  builtin_access::builtin_access (gimple *call, buil
 	dst.basesize = maxobjsize;
     }
 
-  if (src.basesize < 0)
+  if (src.base && src.basesize < 0)
     {
       addr = src.base;
       if (!POINTER_TYPE_P (TREE_TYPE (addr)))
@@ -845,7 +889,7 @@  builtin_access::generic_overlap ()
 
   gcc_assert (dstref->base == srcref->base);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = acs.dstref->maxobjsize;
 
   offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
   gcc_assert (maxsize <= maxobjsize);
@@ -1054,7 +1098,7 @@  builtin_access::strcat_overlap ()
 
   gcc_assert (dstref->base == srcref->base);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = acs.dstref->maxobjsize;
 
   gcc_assert (dstref->base && dstref->base == srcref->base);
 
@@ -1191,7 +1235,7 @@  builtin_access::overlap ()
 {
   builtin_access &acs = *this;
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dstref->maxobjsize;
 
   acs.sizrange[0] = wi::smax (dstref->sizrange[0],
 			      srcref->sizrange[0]).to_shwi ();
@@ -1372,7 +1416,7 @@  maybe_diag_overlap (location_t loc, gimple *call,
 	     "[" HOST_WIDE_INT_PRINT_DEC ", " HOST_WIDE_INT_PRINT_DEC "]",
 	     ovloff[0], ovloff[1]);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dstref.maxobjsize;
   bool must_overlap = ovlsiz[0] > 0;
 
   if (ovlsiz[1] == 0)
@@ -1581,7 +1625,7 @@  static bool
 maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict,
 			  const builtin_memref &ref, bool do_warn)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = ref.maxobjsize;
 
   /* Check for excessive size first and regardless of warning options
      since the result is used to make codegen decisions.  */
@@ -1690,8 +1734,6 @@  maybe_diag_access_bounds (location_t loc, gimple *
     }
   else if (oobref == ref.base)
     {
-      const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
       /* True when the offset formed by an access to the reference
 	 is out of bounds, rather than the initial offset wich is
 	 in bounds.  This implies access past the end.  */
@@ -1814,6 +1856,12 @@  wrestrict_dom_walker::check_call (gimple *call)
       bnd_idx = 2;
       break;
 
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_MEMSET_CHK:
+      dst_idx = 0;
+      bnd_idx = 2;
+      break;
+
     case BUILT_IN_STPCPY:
     case BUILT_IN_STPCPY_CHK:
     case BUILT_IN_STRCPY:
@@ -1844,7 +1892,7 @@  wrestrict_dom_walker::check_call (gimple *call)
 
   /* DST and SRC can be null for a call with an insufficient number
      of arguments to a built-in function declared without a protype.  */
-  if (!dst || !src)
+  if (!dst || (src_idx < nargs && !src))
     return;
 
   /* DST, SRC, or DSTWR can also have the wrong type in a call to
@@ -1851,7 +1899,7 @@  wrestrict_dom_walker::check_call (gimple *call)
      a function declared without a prototype.  Avoid checking such
      invalid calls.  */
   if (TREE_CODE (TREE_TYPE (dst)) != POINTER_TYPE
-      || TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE
+      || (src && TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE)
       || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
@@ -1901,15 +1949,23 @@  check_bounds_or_overlap (gimple *call, tree dst, t
       return OPT_Warray_bounds;
     }
 
-  bool check_overlap
-    = (warn_restrict
-       && (bounds_only
-	   || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
-	       && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
-
-  if (!check_overlap)
+  if (!warn_restrict || bounds_only || !src)
     return 0;
 
+  if (!bounds_only)
+    {
+      switch (DECL_FUNCTION_CODE (func))
+	{
+	case BUILT_IN_MEMMOVE:
+	case BUILT_IN_MEMMOVE_CHK:
+	case BUILT_IN_MEMSET:
+	case BUILT_IN_MEMSET_CHK:
+	  return 0;
+	default:
+	  break;
+	}
+    }
+
   if (operand_equal_p (dst, src, 0))
     {
       /* Issue -Wrestrict unless the pointers are null (those do
Index: gcc/testsuite/gcc.dg/Wstringop-overflow.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow.c	(working copy)
@@ -51,8 +51,8 @@  void test_memcpy_array (const void *s)
   T (a7 + UR (8, 9), s, 7);   /* { dg-warning "writing 7 bytes into a region of size 0" } */
 
   T (a7 + UR (9, 10), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, SIZE_MAX), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" } */
+  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
+  T (a7 + UR (DIFF_MAX, SIZE_MAX), s, 7);  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-*} } */
 
   /* This is valid.  */
   char *d = a7 + 7;
@@ -102,8 +102,8 @@  void test_strcpy_array (void)
   T (a7 + UR (8, 9), "012345");   /* { dg-warning "writing 7 bytes into a region of size 0" } */
 
   T (a7 + UR (9, 10), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" } */
-  T (a7 + UR (DIFF_MAX, SIZE_MAX), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" } */
+  T (a7 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
+  T (a7 + UR (DIFF_MAX, SIZE_MAX), "012345");  /* { dg-warning "writing 7 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
 
   char *d = a7 + 7;
 
@@ -127,6 +127,6 @@  void test_strncpy_memarray (struct MemArray *p, co
   T (p->a9 + UR (9, 10), s, 9);   /* { dg-warning "writing 9 bytes into a region of size 0" } */
   T (p->a9 + UR (10, 11), s, 9);  /* { dg-warning "writing 9 bytes into a region of size 0" } */
 
-  T (p->a9 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 1);  /* { dg-warning "writing 1 byte into a region of size 0" } */
-  T (p->a9 + UR (DIFF_MAX, SIZE_MAX), s, 3);  /* { dg-warning "writing 3 bytes into a region of size 0" } */
+  T (p->a9 + UR (DIFF_MAX, DIFF_MAX + (size_t)1), s, 1);  /* { dg-warning "writing 1 byte into a region of size 0" "pr85350" { xfail *-*-* } } */
+  T (p->a9 + UR (DIFF_MAX, SIZE_MAX), s, 3);  /* { dg-warning "writing 3 bytes into a region of size 0" "pr85350" { xfail *-*-* } } */
 }
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-10.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-10.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-10.c	(working copy)
@@ -0,0 +1,120 @@ 
+/* PR tree-optimization/89350 - Wrong -Wstringop-overflow warning
+   on a variable offset from the end of an array
+   Test exercising -Wstringop-truncation alone, with -Warray-bounds
+   explicitly disabled.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds -Wstringop-overflow -ftrack-macro-expansion=0" }  */
+
+#include "range.h"
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+
+void sink (void*);
+
+extern char ga7[7];
+
+
+#define T(d, n) (memcpy ((d), s, (n)), sink (d))
+
+void test_memcpy_array_cst_range_off (const void *s)
+{
+  char *d = ga7 + 1;
+
+  T (d + UR (1, 2), 1);
+  T (d + UR (1, 2), 5);
+
+  T (d + UR (0, 1), 6);
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  T (d + UR (1, 2), 6);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
+  T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
+
+  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 1);
+  T (d + SR (-2, -1), 2);     /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+
+  d = ga7 + 7;
+  T (d + SR (-7, -6), 1);
+  T (d + SR (-7, -1), 1);
+  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+
+  T (d + UR (1, 2), 1);       /* { dg-warning "writing 1 byte into a region of size 0 " "pr89350" { xfail *-*-* } } */
+}
+
+
+void test_memcpy_array_range_range_off (const void *s)
+{
+  char *d = ga7 + UR (0, 1);
+  T (d + SR (-1, 0), 1);
+  T (d + SR (-1, 0), 7);
+  T (d + SR (-1, 0), 9);       /* { dg-warning "writing 1 byte into a region of size 0 " "pr89350" { xfail *-*-* } } */
+}
+
+
+#undef T
+#define T(d, n) (memset ((d), 0, (n)), sink (d))
+
+void test_memset_array_unsigned_off (void)
+{
+  char *d = ga7 + 1;
+
+  T (d + UR (1, 2), 1);
+  T (d + UR (1, 2), 5);
+
+  T (d + UR (0, 1), 6);
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  T (d + UR (1, 2), 6);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */
+  T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
+
+  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 1);
+  T (d + SR (-2, -1), 2);     /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+
+  d = ga7 + 7;
+  T (d + SR (-7, -6), 1);
+  T (d + SR (-7, -1), 1);
+  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+
+  T (d + UR (1, 2), 1);       /* { dg-warning "writing 1 byte into a region of size 0 " } */
+}
+
+
+
+struct MemArray { char a7[7], a3[3], c; };
+
+extern struct MemArray gma;
+
+void test_memset_memarray (void)
+{
+  char *d = gma.a7 + 1;
+
+  T (d + UR (1, 2), 1);
+  T (d + UR (1, 2), 5);
+
+  T (d + UR (0, 1), 6);
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  T (d + UR (1, 2), 6);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89350" { xfail *-*-* } } */
+  T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " "pr85350" { xfail *-*-* } } */
+
+}
+
+
+#undef T
+#define T(d, n) (memcpy ((d), s, (n)), sink (d))
+
+void test_memcpy_array_signed_off (const void *s)
+{
+  char *d = ga7 + 1;
+
+  T (d + SR (-7, 7), 7);
+  T (d + SR (-1, 1), 7);
+  T (d + SR (-1, 1), 9);      /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-1, 2), 9);      /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
+  T (d + SR (1, 2), 1);
+  T (d + SR (1, 2), 5);
+
+  T (d + SR (0, 1), 6);
+  T (d + UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-11.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-11.c	(working copy)
@@ -0,0 +1,321 @@ 
+/* PR tree-optimization/89350 - Wrong -Wstringop-overflow warning
+   on a variable offset from the end of an array
+   Test exercising -Wstringop-truncation with -Wall.
+   -Wstringop-truncation is disabled to avoid warnings for strncpy
+   calls whose bound matches the size of the destination getting
+   in the way of -Wstringop-overflow.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-stringop-truncation -ftrack-macro-expansion=0" }  */
+
+#include "range.h"
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+extern char* strcpy (char*, const char*);
+extern char* strncpy (char*, const char*, size_t);
+
+void sink (void*);
+
+#define CAT(pfx, line) pfx ## line
+#define CONCAT(pfx, line) CAT (pfx, line)
+#define UNIQ_NAME(pfx) CONCAT (pfx, __LINE__)
+
+/* Exercise a call to memset with a distinct destination object each
+   time to prevent GCC from reusing the destination pointer in later
+   tests.  */
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    memset (d, 0, n);				\
+    sink (d);					\
+  } while (0)
+
+
+/* Exercise calls to memset with a destination pointer pointing to
+   an array plus constant offset plus variable offset, in that order.  */
+
+void test_memset_array_cst_range_off (void)
+{
+  T (1, SR (-7, 7), 7);
+  T (1, SR (-1, 1), 7);
+  T (1, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 1);
+  T (1, SR ( 1, 2), 5);
+
+  T (1, SR ( 0, 1), 6);
+  T (1, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR (-7, 7), 7);
+  T (2, SR (-2, 7), 7);
+  T (2, SR (-1, 1), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 1);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);
+  T (2, SR ( 1, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 7);
+  T (7, UR (-7, 0), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 3);
+  T (7, UR (-2, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+/* Exercise calls to memset with a destination pointer pointing to
+   an array plus variable offset plus constant offset.  */
+
+void test_memset_array_range_cst_off (void)
+{
+  T (SR (-7, 7), 1, 7);
+  T (SR (-1, 1), 1, 7);
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 5);
+
+  T (SR ( 0, 1), 1, 6);
+  T (UR ( 1, 2), 1, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 7);
+  T (SR (-1, 1), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);
+  T (SR ( 1, 2), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_memset_array_range_range_off (void)
+{
+  T (UR (0, 1), UR (0, 1), 7);
+  T (UR (3, 5), UR (2, 7), 1);
+  T (UR (3, 7), UR (2, 9), 2);
+  T (UR (3, 9), UR (2, 9), 3);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (UR (0, 1), UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+#undef T
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    memcpy (d, s, n);				\
+    sink (d);					\
+  } while (0)
+
+
+void test_memcpy_array_cst_range_off (const void *s)
+{
+  T (1, SR (-7, 7), 7);
+  T (1, SR (-1, 1), 7);
+  T (1, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 1);
+  T (1, SR ( 1, 2), 5);
+
+  T (1, SR ( 0, 1), 6);
+  T (1, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR (-7, 7), 7);
+  T (2, SR (-2, 7), 7);
+  T (2, SR (-1, 1), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 1);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);
+  T (2, SR ( 1, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 7);
+  T (7, UR (-7, 0), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 3);
+  T (7, UR (-2, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_memcpy_array_range_cst_off (const void *s)
+{
+  T (SR (-7, 7), 1, 7);
+  T (SR (-1, 1), 1, 7);
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 5);
+
+  T (SR ( 0, 1), 1, 6);
+  T (UR ( 1, 2), 1, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 7);
+  T (SR (-1, 1), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);
+  T (SR ( 1, 2), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_memcpy_array_range_range_off (const void *s)
+{
+  T (UR (0, 1), UR (0, 1), 7);
+  T (UR (3, 5), UR (2, 7), 1);
+  T (UR (3, 7), UR (2, 9), 2);
+  T (UR (3, 9), UR (2, 9), 3);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (UR (0, 1), UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+#undef T
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    const char str[] = "0123456789";		\
+    const char *s = str + sizeof str - 1 - n;   \
+    strcpy (d, s);				\
+    sink (d);					\
+  } while (0)
+
+
+void test_strcpy_array_cst_range_off (void)
+{
+  T (1, SR (-7, 7), 6);
+  T (1, SR (-1, 1), 6);
+  T (1, SR (-1, 1), 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 0);
+  T (1, SR ( 1, 2), 4);
+
+  T (1, SR ( 0, 1), 5);
+  T (1, UR ( 1, 2), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR (-7, 7), 6);
+  T (2, SR (-2, 7), 6);
+  T (2, SR (-1, 1), 6);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR (-1, 1), 8);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 0);
+  T (2, SR ( 1, 2), 2);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 6);
+  T (7, UR (-7, 0), 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 2);
+  T (7, UR (-2, 2), 4);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_strcpy_array_range_cst_off (const char *s)
+{
+  T (SR (-7, 7), 1, 6);
+  T (SR (-1, 1), 1, 6);
+  T (SR (-1, 1), 1, 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 0);
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 4);
+
+  T (SR ( 0, 1), 1, 5);
+  T (UR ( 1, 2), 1, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 6);
+  T (SR (-1, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 0);
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 2);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+#undef T
+#define T(off1, off2, n)			\
+  do {						\
+    extern char UNIQ_NAME (ga)[7];		\
+    char *d = UNIQ_NAME (ga) + off1;		\
+    d += off2;					\
+    strncpy (d, s, n);				\
+    sink (d);					\
+  } while (0)
+
+
+void test_strncpy_array_cst_range_off (const char *s)
+{
+  T (1, SR (-7, 7), 7);
+  T (1, SR (-1, 1), 7);
+  T (1, SR (-1, 1), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (1, SR ( 1, 2), 1);
+  T (1, SR ( 1, 2), 5);
+
+  T (1, SR ( 0, 1), 6);
+  T (1, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( -7, 7), 7);
+  T (2, SR ( -1, 1), 7);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( -1, 1), 9);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (2, SR ( 1, 2), 1);
+  T (2, SR ( 1, 2), 3);
+  T (2, SR ( 1, 2), 4);
+  T (2, SR ( 1, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (2, SR ( 0, 1), 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
+  T (2, UR ( 1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-7, 0), 7);
+  T (7, UR (-7, 0), 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (7, UR (-3, 2), 3);
+  T (7, UR (-2, 2), 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_strncpy_array_range_cst_off (const char *s)
+{
+  T (SR (-7, 7), 1, 7);
+  T (SR (-1, 1), 1, 7);
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR ( 1, 2), 1, 1);
+  T (SR ( 1, 2), 1, 5);
+
+  T (SR ( 0, 1), 1, 6);
+  T (UR ( 1, 2), 1, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR (-7, 7), 2, 7);
+  T (SR (-1, 1), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR (-1, 1), 2, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (SR ( 1, 2), 2, 1);
+  T (SR ( 1, 2), 2, 3);
+  T (SR ( 1, 2), 2, 4);
+  T (SR ( 1, 2), 2, 5);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (SR ( 0, 1), 2, 6);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+
+  T (UR ( 1, 2), 2, 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
+
+
+void test_strncpy_array_range_range_off (const char *s)
+{
+  T (UR (0, 1), UR (0, 1), 7);
+  T (UR (3, 5), UR (2, 7), 1);
+  T (UR (3, 7), UR (2, 9), 2);
+  T (UR (3, 9), UR (2, 9), 3);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  T (UR (0, 1), UR (1, 2), 7);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+}
Index: gcc/testsuite/gcc.dg/pr40340-1.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-1.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-1.c	(working copy)
@@ -20,5 +20,5 @@  main (void)
   return 0;
 }
 
-/* { dg-warning "writing" "" { target *-*-* } 10 } */
+/* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
Index: gcc/testsuite/gcc.dg/pr40340-2.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-2.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-2.c	(working copy)
@@ -12,5 +12,5 @@  main (void)
   return 0;
 }
 
-/* { dg-warning "writing" "" { target *-*-* } 10 } */
+/* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
Index: gcc/testsuite/gcc.dg/pr40340-4.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-4.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-4.c	(working copy)
@@ -1,6 +1,6 @@ 
 /* PR middle-end/40340 */
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall -Wno-system-headers -g" } */
+/* { dg-options "-O2 -Wall -Wno-array-bounds -Wno-system-headers -g" } */
 
 #define TEST3
 #include "pr40340.h"
Index: gcc/testsuite/gcc.dg/pr40340-5.c
===================================================================
--- gcc/testsuite/gcc.dg/pr40340-5.c	(revision 269190)
+++ gcc/testsuite/gcc.dg/pr40340-5.c	(working copy)
@@ -13,5 +13,5 @@  main (void)
   return 0;
 }
 
-/* { dg-warning "writing" "" { target *-*-* } 10 } */
+/* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */