diff mbox series

Reset PHI base0 flag if it's clear in any argument [PR101977]

Message ID 10f5cc43-ab5c-85fb-0d52-b2fbf378c7c3@gmail.com
State New
Headers show
Series Reset PHI base0 flag if it's clear in any argument [PR101977] | expand

Commit Message

Martin Sebor Aug. 23, 2021, 11:40 p.m. UTC
When determining the properties of objects referenced by a PHI's
arguments, compute_objsize() has logic to filter out null pointers.
It also has special logic that tries to deal with arguments that
refer to the same object (as opposed to different objects).  A bug
in the former prevents the function from clearing the flag called
BASE0 that indicates that the identities of all the objects are
known.  The latter logic turns out to be redundant but its presence
make the logic in the function harder to follow.

The attached patch corrects the former logic by resetting the BASE0
flag for a PHI result if it's clear for any of its arguments.  It
also does away with the latter logic, simplifying the code.  Testing
the patch exposed a couple of other, minor, bugs in using an object's
total size without considering an offset into it, and failing to reset
members of reused access_ref objects.

Tested on x86_64-linux.

Martin

Comments

Richard Biener Aug. 24, 2021, 7:58 a.m. UTC | #1
On Tue, Aug 24, 2021 at 1:41 AM Martin Sebor via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> When determining the properties of objects referenced by a PHI's
> arguments, compute_objsize() has logic to filter out null pointers.
> It also has special logic that tries to deal with arguments that
> refer to the same object (as opposed to different objects).  A bug
> in the former prevents the function from clearing the flag called
> BASE0 that indicates that the identities of all the objects are
> known.  The latter logic turns out to be redundant but its presence
> make the logic in the function harder to follow.
>
> The attached patch corrects the former logic by resetting the BASE0
> flag for a PHI result if it's clear for any of its arguments.  It
> also does away with the latter logic, simplifying the code.  Testing
> the patch exposed a couple of other, minor, bugs in using an object's
> total size without considering an offset into it, and failing to reset
> members of reused access_ref objects.
>
> Tested on x86_64-linux.

OK.

Richard.

> Martin
diff mbox series

Patch

Reset PHI base0 flag if it's clear in any argument [PR101977, ...]

Resolves:
PR middle-end/101600 - Spurious -Warray-bounds downcasting a polymorphic pointer
PR middle-end/101977 - bogus -Warray-bounds on a negative index into a parameter in conditional with null

gcc/ChangeLog:

	PR middle-end/101600
	PR middle-end/101977
	* gimple-ssa-warn-access.cc (maybe_warn_for_bound): Tighten up
	the phrasing of a warning.
	(check_access): Use the remaining size after subtracting any offset
	rather than the whole object size.
	* pointer-query.cc (access_ref::get_ref): Clear BASE0 flag if it's
	clear for any nonnull PHI argument.
	(compute_objsize): Clear argument.

gcc/testsuite/ChangeLog:

	PR middle-end/101600
	PR middle-end/101977
	* g++.dg/pr100574.C: Prune out valid warning.
	* gcc.dg/pr20126.c: Same.
	* gcc.dg/Wstringop-overread.c: Adjust text of expected warnings.
	Add new instances.
	* gcc.dg/warn-strnlen-no-nul.c: Same.
	* g++.dg/warn/Warray-bounds-26.C: New test.
	* gcc.dg/Warray-bounds-88.c: New test.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 4a2dd9ade77..5df97a6473a 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -704,6 +704,15 @@  maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
   if (opt == OPT_Wstringop_overread)
     {
       bool maybe = pad && pad->src.phi ();
+      if (maybe)
+	{
+	  /* Issue a "maybe" warning only if the PHI refers to objects
+	     at least one of which has more space remaining than the bound.
+	     Otherwise, if the bound is greater, use the definitive form.  */
+	  offset_int remmax = pad->src.size_remaining ();
+	  if (remmax < wi::to_offset (bndrng[0]))
+	    maybe = false;
+	}
 
       if (tree_int_cst_lt (maxobjsize, bndrng[0]))
 	{
@@ -788,6 +797,15 @@  maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
     }
 
   bool maybe = pad && pad->dst.phi ();
+  if (maybe)
+    {
+      /* Issue a "maybe" warning only if the PHI refers to objects
+	 at least one of which has more space remaining than the bound.
+	 Otherwise, if the bound is greater, use the definitive form.  */
+      offset_int remmax = pad->dst.size_remaining ();
+      if (remmax < wi::to_offset (bndrng[0]))
+	maybe = false;
+    }
   if (tree_int_cst_lt (maxobjsize, bndrng[0]))
     {
       if (bndrng[0] == bndrng[1])
@@ -1418,7 +1436,7 @@  check_access (GimpleOrTree exp, tree dstwrite,
       location_t loc = get_location (exp);
       tree size = dstsize;
       if (pad && pad->mode == access_read_only)
-	size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
+	size = wide_int_to_tree (sizetype, pad->src.size_remaining ());
 
       if (range[0] && maxread && tree_fits_uhwi_p (size))
 	{
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 99caf78bfa7..ba8f8a9dc69 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -634,10 +634,10 @@  access_ref::phi () const
   return as_a <gphi *> (def_stmt);
 }
 
-/* Determine and return the largest object to which *THIS.  If *THIS
-   refers to a PHI and PREF is nonnull, fill *PREF with the details
-   of the object determined by compute_objsize(ARG, OSTYPE) for each
-   PHI argument ARG.  */
+/* Determine and return the largest object to which *THIS refers.  If
+   *THIS refers to a PHI and PREF is nonnull, fill *PREF with the details
+   of the object determined by compute_objsize(ARG, OSTYPE) for each PHI
+   argument ARG.  */
 
 tree
 access_ref::get_ref (vec<access_ref> *all_refs,
@@ -659,21 +659,25 @@  access_ref::get_ref (vec<access_ref> *all_refs,
   if (!psnlim->visit_phi (ref))
     return NULL_TREE;
 
-  /* Reflects the range of offsets of all PHI arguments refer to the same
-     object (i.e., have the same REF).  */
-  access_ref same_ref;
-  /* The conservative result of the PHI reflecting the offset and size
-     of the largest PHI argument, regardless of whether or not they all
-     refer to the same object.  */
   pointer_query empty_qry;
   if (!qry)
     qry = &empty_qry;
 
+  /* The conservative result of the PHI reflecting the offset and size
+     of the largest PHI argument, regardless of whether or not they all
+     refer to the same object.  */
   access_ref phi_ref;
   if (pref)
     {
+      /* The identity of the object has not been determined yet but
+	 PREF->REF is set by the caller to the PHI for convenience.
+	 The size is negative/invalid and the offset is zero (it's
+	 updated only after the identity of the object has been
+	 established).  */
+      gcc_assert (pref->sizrng[0] < 0);
+      gcc_assert (pref->offrng[0] == 0 && pref->offrng[1] == 0);
+
       phi_ref = *pref;
-      same_ref = *pref;
     }
 
   /* Set if any argument is a function array (or VLA) parameter not
@@ -682,8 +686,6 @@  access_ref::get_ref (vec<access_ref> *all_refs,
   /* The size of the smallest object referenced by the PHI arguments.  */
   offset_int minsize = 0;
   const offset_int maxobjsize = wi::to_offset (max_object_size ());
-  /* The offset of the PHI, not reflecting those of its arguments.  */
-  const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] };
 
   const unsigned nargs = gimple_phi_num_args (phi_stmt);
   for (unsigned i = 0; i < nargs; ++i)
@@ -695,28 +697,31 @@  access_ref::get_ref (vec<access_ref> *all_refs,
 	/* A PHI with all null pointer arguments.  */
 	return NULL_TREE;
 
-      /* Add PREF's offset to that of the argument.  */
-      phi_arg_ref.add_offset (orng[0], orng[1]);
       if (TREE_CODE (arg) == SSA_NAME)
 	qry->put_ref (arg, phi_arg_ref);
 
       if (all_refs)
 	all_refs->safe_push (phi_arg_ref);
 
-      const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
-				   || phi_arg_ref.sizrng[1] != maxobjsize);
-
       parmarray |= phi_arg_ref.parmarray;
 
       const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
 
       if (phi_ref.sizrng[0] < 0)
 	{
+	  /* If PHI_REF doesn't contain a meaningful result yet set it
+	     to the result for the first argument.  */
 	  if (!nullp)
-	    same_ref = phi_arg_ref;
-	  phi_ref = phi_arg_ref;
+	    phi_ref = phi_arg_ref;
+
+	  /* Set if the current argument refers to one or more objects of
+	     known size (or range of sizes), as opposed to referring to
+	     one or more unknown object(s).  */
+	  const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
+				       || phi_arg_ref.sizrng[1] != maxobjsize);
 	  if (arg_known_size)
 	    minsize = phi_arg_ref.sizrng[0];
+
 	  continue;
 	}
 
@@ -740,8 +745,10 @@  access_ref::get_ref (vec<access_ref> *all_refs,
       offset_int phirem[2];
       phirem[1] = phi_ref.size_remaining (phirem);
 
-      if (phi_arg_ref.ref != same_ref.ref)
-	same_ref.ref = NULL_TREE;
+      /* Reset the PHI's BASE0 flag if any of the nonnull arguments
+	 refers to an object at an unknown offset.  */
+      if (!phi_arg_ref.base0)
+	phi_ref.base0 = false;
 
       if (phirem[1] < argrem[1]
 	  || (phirem[1] == argrem[1]
@@ -749,32 +756,13 @@  access_ref::get_ref (vec<access_ref> *all_refs,
 	/* Use the argument with the most space remaining as the result,
 	   or the larger one if the space is equal.  */
 	phi_ref = phi_arg_ref;
-
-      /* Set SAME_REF.OFFRNG to the maximum range of all arguments.  */
-      if (phi_arg_ref.offrng[0] < same_ref.offrng[0])
-	same_ref.offrng[0] = phi_arg_ref.offrng[0];
-      if (same_ref.offrng[1] < phi_arg_ref.offrng[1])
-	same_ref.offrng[1] = phi_arg_ref.offrng[1];
     }
 
-  if (!same_ref.ref && same_ref.offrng[0] != 0)
-    /* Clear BASE0 if not all the arguments refer to the same object and
-       if not all their offsets are zero-based.  This allows the final
-       PHI offset to out of bounds for some arguments but not for others
-       (or negative even of all the arguments are BASE0), which is overly
-       permissive.  */
-    phi_ref.base0 = false;
-
-  if (same_ref.ref)
-    phi_ref = same_ref;
-  else
-    {
-      /* Replace the lower bound of the largest argument with the size
-	 of the smallest argument, and set PARMARRAY if any argument
-	 was one.  */
-      phi_ref.sizrng[0] = minsize;
-      phi_ref.parmarray = parmarray;
-    }
+  /* Replace the lower bound of the largest argument with the size
+     of the smallest argument, and set PARMARRAY if any argument
+     was one.  */
+  phi_ref.sizrng[0] = minsize;
+  phi_ref.parmarray = parmarray;
 
   if (phi_ref.sizrng[0] < 0)
     {
@@ -804,6 +792,14 @@  access_ref::size_remaining (offset_int *pmin /* = NULL */) const
   if (!pmin)
     pmin = &minbuf;
 
+  if (sizrng[0] < 0)
+    {
+      /* If the identity of the object hasn't been determined return
+	 the maximum size range.  */
+      *pmin = 0;
+      return wi::to_offset (max_object_size ());
+    }
+
   /* add_offset() ensures the offset range isn't inverted.  */
   gcc_checking_assert (offrng[0] <= offrng[1]);
 
@@ -1597,6 +1593,11 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
     {
       pref->ref = ptr;
 
+      /* Reset the offset in case it was set by a prior call and not
+	 cleared by the caller.  The offset is only adjusted after
+	 the identity of the object has been determined.  */
+      pref->offrng[0] = pref->offrng[1] = 0;
+
       if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
 	{
 	  /* Set the maximum size if the reference is to the pointer
@@ -1607,6 +1608,9 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  return true;
 	}
 
+      /* Valid offsets into the object are nonnegative.  */
+      pref->base0 = true;
+
       if (tree size = decl_init_size (ptr, false))
 	if (TREE_CODE (size) == INTEGER_CST)
 	  {
@@ -1960,6 +1964,11 @@  compute_objsize (tree ptr, int ostype, access_ref *pref,
 {
   pointer_query qry;
   qry.rvals = rvals;
+
+  /* Clear and invalidate in case *PREF is being reused.  */
+  pref->offrng[0] = pref->offrng[1] = 0;
+  pref->sizrng[0] = pref->sizrng[1] = -1;
+
   ssa_name_limit_t snlim;
   if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
     return NULL_TREE;
@@ -1982,6 +1991,10 @@  compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
   else
     ptr_qry = &qry;
 
+  /* Clear and invalidate in case *PREF is being reused.  */
+  pref->offrng[0] = pref->offrng[1] = 0;
+  pref->sizrng[0] = pref->sizrng[1] = -1;
+
   ssa_name_limit_t snlim;
   if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
     return NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/pr100574.C b/gcc/testsuite/g++.dg/pr100574.C
index 42ba0404a28..0df62aa70ad 100644
--- a/gcc/testsuite/g++.dg/pr100574.C
+++ b/gcc/testsuite/g++.dg/pr100574.C
@@ -40,6 +40,8 @@  template <typename _Tp, typename _Alloc>
 template <typename...>
 void vector<_Tp, _Alloc>::_M_realloc_insert() {
   __alloc_traits::pointer __trans_tmp_5;
+  /* __len is used uninitialized below, which might trigger warnings,
+     even without -Wall (and other than -Wuninitialized).  */
   long __len(__len || max_size()), __elems_before;
   __trans_tmp_5 = _M_allocate___n
     ? __alloc_traits::allocate(_M_impl, _M_allocate___n)
@@ -62,3 +64,5 @@  void ReadTrackChunk()
     case MIDIST_PITCHBEND:
       block.data.push_back();
 }
+
+// { dg-prune-output "warning" }
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-26.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-26.C
new file mode 100644
index 00000000000..f72ac9d4b51
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-26.C
@@ -0,0 +1,27 @@ 
+/* PR middle-end/101600 - Spurious -Warray-bounds downcasting a polymorphic
+   pointer
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct S1 { virtual ~S1(); };
+struct S2 { int m; };
+struct S3 { virtual ~S3(); };
+struct S4: S1, S2, S3 {};
+
+int f1 ();
+
+void f2 (S3 *);
+
+void f3 (S2 *p)
+{
+  for (int i = f1 (); f1 (); )
+    {
+      if (i == 0)
+  	{
+  	  p = 0;
+  	  break;
+  	}
+    }
+
+  f2 (static_cast<S4 *>(p));  // { dg-bogus "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-88.c b/gcc/testsuite/gcc.dg/Warray-bounds-88.c
new file mode 100644
index 00000000000..8cee8d28571
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-88.c
@@ -0,0 +1,134 @@ 
+/* PR middle-end/101977 - bogus -Warray-bounds on a negative index into
+   a parameter in conditional with null
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct A { int i; };
+struct B { struct A a1; struct A a2; };
+
+
+void nowarn_p_0_0 (struct A *p, int i)
+{
+  struct A *q = i < 0 ? p : 0 < i ? (struct A*)0 : 0;
+  struct B *r = (struct B*)((char *)q - __builtin_offsetof (struct B, a2));
+  r->a1.i = 0;
+}
+
+void nowarn_0_p_0 (struct A *p, int i)
+{
+  struct A *q = i < 0 ? 0 : 0 < i ? p : 0;
+  struct B *r = (struct B*)((char *)q - __builtin_offsetof (struct B, a2));
+  r->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_0_0_p (struct A *p, int i)
+{
+  struct A *q = i < 0 ? 0 : 0 < i ? 0 : p;
+  struct B *r = (struct B*)((char *)q - __builtin_offsetof (struct B, a2));
+  r->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+
+void nowarn_p_q_0 (struct A *p, struct A *q, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? q : 0;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_p_0_q (struct A *p, struct A *q, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? 0 : q;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_0_p_q (struct A *p, struct A *q, int i)
+{
+  struct A *r = i < 0 ? 0 : 0 < i ? p : q;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;
+}
+
+
+void nowarn_p_q_r (struct A *p, struct A *q, struct A *r, int i)
+{
+  struct A *s = i < 0 ? p : 0 < i ? q : r;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+
+extern struct B b1, b2, b3;
+
+void nowarn_p_b1_0 (struct A *p, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? &b1.a2 : 0;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_p_0_b1 (struct A *p, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? 0 : &b1.a2;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_0_p_b1 (struct A *p, int i)
+{
+  struct A *r = i < 0 ? 0 : 0 < i ? p : &b1.a2;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;
+}
+
+
+void nowarn_p_b1_b2 (struct A *p, int i)
+{
+  struct A *s = i < 0 ? p : 0 < i ? &b1.a2 : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void nowarn_b1_p_b2 (struct A *p, int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? p : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void nowarn_b1_b2_p (struct A *p, int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? &b2.a2 : p;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void nowarn_b1_b2_b3 (struct A *p, int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? &b2.a2 : &b3.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+
+void nowarn_0_b1_b2 (int i)
+{
+  struct A *s = i < 0 ? 0 : 0 < i ? &b1.a2 : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void warn_b1_0_b2 (int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? 0 : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void warn_b1_b2_0 (int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? &b2.a2 : 0;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread.c b/gcc/testsuite/gcc.dg/Wstringop-overread.c
index 0343e43cce4..7db74029819 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overread.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overread.c
@@ -317,9 +317,9 @@  void test_strnlen_array (int i, int i0, unsigned n)
   T (strnlen (a1, n));
 
   T (strnlen (a1 + 1, 0));
-  T (strnlen (a1 + 1, 1));      // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a1 + 1, 1));      // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a1 + 1, i0));
-  T (strnlen (a1 + 1, i0 + 1)); // { dg-warning "'strnlen' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strnlen (a1 + 1, i0 + 1)); // { dg-warning "'strnlen' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strnlen (a1 + 1, n));
   T (strnlen (a1 + i, 0));
   T (strnlen (a1 + i, 1));
@@ -335,7 +335,7 @@  void test_strnlen_array (int i, int i0, unsigned n)
   T (strnlen (a1 + i0, 2));     // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" }
   T (strnlen (a1 + i0, n));
   T (strnlen (a1 + i0 + 1, 0));
-  T (strnlen (a1 + i0 + 1, 1)); // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a1 + i0 + 1, 1)); // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a1 + i0 + 1, n));
 
   T (strnlen (a2, 0));
@@ -344,10 +344,10 @@  void test_strnlen_array (int i, int i0, unsigned n)
   T (strnlen (a2, n));
   T (strnlen (a2 + 1, 0));
   T (strnlen (a2 + 1, 1));
-  T (strnlen (a2 + 1, 2));      // { dg-warning "'strnlen' specified bound 2 exceeds source size 1"  "pr87492" { xfail *-*-* } }
+  T (strnlen (a2 + 1, 2));      // { dg-warning "'strnlen' specified bound 2 exceeds source size 1"  "pr87492" }
   T (strnlen (a2 + 1, n));
   T (strnlen (a2 + 2, 0));
-  T (strnlen (a2 + 2, 1));      // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a2 + 2, 1));      // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a2 + 2, n));
   T (strnlen (a2 + i, 0));
   T (strnlen (a2 + i, 1));
@@ -365,13 +365,13 @@  void test_strnlen_array (int i, int i0, unsigned n)
 
   T (strnlen (a2 + i0 + 1, 0));
   T (strnlen (a2 + i0 + 1, 1));
-  T (strnlen (a2 + i0 + 1, 2));
+  T (strnlen (a2 + i0 + 1, 2)); // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" }
   T (strnlen (a2 + i0 + 1, n));
 
   T (strnlen (a2 + i0 + 2, 0));
-  T (strnlen (a2 + i0 + 2, 1)); // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a2 + i0 + 2, 1)); // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a2 + i0 + 2, i0));
-  T (strnlen (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strnlen' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strnlen (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strnlen' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strnlen (a2 + i0 + 2, n));
 }
 
@@ -512,9 +512,9 @@  void test_strndup_array (int i, int i0, unsigned n)
   T (strndup (a1, 2));          // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
   T (strndup (a1, n));
   T (strndup (a1 + 1, 0));
-  T (strndup (a1 + 1, 1));      // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a1 + 1, 1));      // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a1 + 1, i0));
-  T (strndup (a1 + 1, i0 + 1)); // { dg-warning "'strndup' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strndup (a1 + 1, i0 + 1)); // { dg-warning "'strndup' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strndup (a1 + 1, n));
   T (strndup (a1 + i, 0));
   T (strndup (a1 + i, 1));
@@ -529,7 +529,7 @@  void test_strndup_array (int i, int i0, unsigned n)
   T (strndup (a1 + i0, 1));
   T (strndup (a1 + i0, n));
   T (strndup (a1 + i0 + 1, 0));
-  T (strndup (a1 + i0 + 1, 1)); // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a1 + i0 + 1, 1)); // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a1 + i0 + 1, n));
 
   T (strndup (a2, 0));
@@ -538,10 +538,10 @@  void test_strndup_array (int i, int i0, unsigned n)
   T (strndup (a2, n));
   T (strndup (a2 + 1, 0));
   T (strndup (a2 + 1, 1));
-  T (strndup (a2 + 1, 2));
+  T (strndup (a2 + 1, 2));      // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
   T (strndup (a2 + 1, n));
   T (strndup (a2 + 2, 0));
-  T (strndup (a2 + 2, 1));      // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a2 + 2, 1));      // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a2 + 2, n));
   T (strndup (a2 + i, 0));
   T (strndup (a2 + i, 1));
@@ -559,13 +559,13 @@  void test_strndup_array (int i, int i0, unsigned n)
 
   T (strndup (a2 + i0 + 1, 0));
   T (strndup (a2 + i0 + 1, 1));
-  T (strndup (a2 + i0 + 1, 2));
+  T (strndup (a2 + i0 + 1, 2)); // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
   T (strndup (a2 + i0 + 1, n));
 
   T (strndup (a2 + i0 + 2, 0));
-  T (strndup (a2 + i0 + 2, 1)); // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a2 + i0 + 2, 1)); // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a2 + i0 + 2, i0));
-  T (strndup (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strndup' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strndup (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strndup' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strndup (a2 + i0 + 2, n));
 }
 
diff --git a/gcc/testsuite/gcc.dg/pr20126.c b/gcc/testsuite/gcc.dg/pr20126.c
index a421ce1758d..10aeec74f39 100644
--- a/gcc/testsuite/gcc.dg/pr20126.c
+++ b/gcc/testsuite/gcc.dg/pr20126.c
@@ -34,6 +34,10 @@  foo (S *x, S *y)
   while (e <= g)
     {
       const char *t = e + 1;
+      /* The pointer E below increases but the bound H stays constant,
+	 letting the latter exceed the size remaining in the argument
+	 pointed to by the formed, which might be detected by
+	 -Wstringop-overread.  */
       if (__builtin_memcmp (e, f, h) == 0)
         return 1;
       e = t;
@@ -48,3 +52,5 @@  main (void)
     abort ();
   return 0;
 }
+
+/* { dg-prune-output "-Wstringop-overread" } */
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 2afd2b5feeb..846e9300750 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -143,14 +143,17 @@  T (v0 ? b[1] : "", bsz);
 T (v0 ? b[2] : "", bsz);
 T (v0 ? b[3] : "", bsz);
 
-T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 may exceed source size 5" } */
-T (v0 ? "" : b[1], bsz + 1);
-T (v0 ? "" : b[2], bsz + 1);
-T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 may exceed source size 5" } */
-T (v0 ? b[1] : "", bsz + 1);
-T (v0 ? b[2] : "", bsz + 1);
-T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+/* The warnings below are strictly correct but the strnlen calls are safe
+   because the reads are bounded by the length of the constant arguments.
+   It might make sense to relax the warning to avoid triggering for them.  */
+T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[1], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[2], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[1] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[2] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
 
 T (v0 ? "" : b[i0], bsz);
 T (v0 ? "" : b[i1], bsz);
@@ -164,11 +167,11 @@  T (v0 ? b[i3] : "", bsz);
 T (v0 ? "" : b[i0], bsz + 1);
 T (v0 ? "" : b[i1], bsz + 1);
 T (v0 ? "" : b[i2], bsz + 1);
-T (v0 ? "" : b[i3], bsz + 1);     /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? "" : b[i3], bsz + 1);     /* { dg-warning "bound 6 exceeds source size 5" "pr86937" } */
 T (v0 ? b[i0] : "", bsz + 1);
 T (v0 ? b[i1] : "", bsz + 1);
 T (v0 ? b[i2] : "", bsz + 1);
-T (v0 ? b[i3] : "", bsz + 1);     /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? b[i3] : "", bsz + 1);     /* { dg-warning "bound 6 exceeds source size 5" "pr86937" } */
 
 T (v0 ? "1234" : b[3], bsz);
 T (v0 ? "1234" : b[i3], bsz);
@@ -180,15 +183,15 @@  T (v0 ? b[0] : b[2], bsz);
 T (v0 ? b[2] : b[3], bsz);
 T (v0 ? b[3] : b[2], bsz);
 
-T (v0 ? "1234" : b[3], bsz + 1);  /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[3] : "1234", bsz + 1);  /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? "1234" : b[3], bsz + 1);  /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[3] : "1234", bsz + 1);  /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "bound 6 exceeds source size 5" } */
 
-T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 may exceed source size 5" "pr86937" { xfail *-*-*} } */
-T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
-T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
 
 struct A { char a[5], b[5]; };