correct handling of offsets in PHI expressions [PR103215]

Message ID 0a747b7c-4cff-b643-3b34-5c9e113c7523@redhat.com
State New
Headers show
Series
  • correct handling of offsets in PHI expressions [PR103215]
Related show

Commit Message

Petter Tomner via Gcc-patches Nov. 22, 2021, 11:54 p.m.
In an effort to avoid false positives while still detecting
certain out-of-bounds accesses the warning code that handles
PHI nodes chooses the operand with the most space remaining
as the one representative of the PHI.  That's not right when
the offsets into the operands are unequal, because it overly
constrains the range of offsets that can be substracted from
the pointer.

The attached change corrects the logic here to not only use
the size of the largest operand but also to extend the range
of offsets into it to reflect all operand.  Unfortunately,
as a result of the more conservative offset computation,
the fix leads to a fair number of false negatives.  I tried
to avoid those but couldn't come up with a clean solution
that didn't require design changes, so I defer those to GCC
13.

The diff is relative to the "cleanup" patch submitted below:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html

Tested on x86_64-linux and by building Glibc and confirming
no new warnings.

Martin

Patch

Extend the offset and size of merged object references [PR103215].

Resolves:
PR tree-optimization/103215 - bogus -Warray-bounds with two pointers with different offsets each


gcc/ChangeLog:

	PR tree-optimization/103215
	* pointer-query.cc (access_ref::merge_ref): Extend the offset and
	size of the merged object instead of using the larger.

gcc/testsuite/ChangeLog:

	PR tree-optimization/103215
	* gcc.dg/Wstringop-overflow-58.c: Adjust and xfail expected warnings.
	* gcc.dg/Wstringop-overflow-59.c: Same.
	* gcc.dg/warn-strnlen-no-nul.c: Same.
	* gcc.dg/Warray-bounds-91.c: New test.
	* gcc.dg/Warray-bounds-92.c: New test.
	* gcc.dg/Wstringop-overflow-83.c: New test.
	* gcc.dg/Wstringop-overflow-85.c: New test.

diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 2f305514ddc..7dc6e0141f8 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -686,27 +686,32 @@  access_ref::merge_ref (vec<access_ref> *all_refs, tree arg, gimple *stmt,
   if (known_size && aref.sizrng[0] < minsize)
     minsize = aref.sizrng[0];
 
-  /* Determine the amount of remaining space in the argument.  */
-  offset_int argrem[2];
-  argrem[1] = aref.size_remaining (argrem);
+  /* Extend the size and offset of *THIS to account for AREF.  The result
+     can be cached but results in false negatives.  */
 
-  /* Determine the amount of remaining space computed so far and
-     if the remaining space in the argument is more use it instead.  */
-  offset_int merged_rem[2];
-  merged_rem[1] = size_remaining (merged_rem);
+  offset_int orng[2];
+  if (sizrng[1] < aref.sizrng[1])
+    {
+      orng[0] = offrng[0];
+      orng[1] = offrng[1];
+      *this = aref;
+    }
+  else
+    {
+      orng[0] = aref.offrng[0];
+      orng[1] = aref.offrng[1];
+    }
+
+  if (orng[0] < offrng[0])
+    offrng[0] = orng[0];
+  if (offrng[1] < orng[1])
+    offrng[1] = orng[1];
 
   /* Reset the PHI's BASE0 flag if any of the nonnull arguments
      refers to an object at an unknown offset.  */
   if (!aref.base0)
     base0 = false;
 
-  if (merged_rem[1] < argrem[1]
-      || (merged_rem[1] == argrem[1]
-	  && sizrng[1] < aref.sizrng[1]))
-    /* Use the argument with the most space remaining as the result,
-       or the larger one if the space is equal.  */
-    *this = aref;
-
   sizrng[0] = minsize;
   parmarray = merged_parmarray;
 
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-91.c b/gcc/testsuite/gcc.dg/Warray-bounds-91.c
new file mode 100644
index 00000000000..1c81091b2a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-91.c
@@ -0,0 +1,145 @@ 
+/* PR middle-end/103215 - bogus -Warray-bounds with two pointers with
+   different offsets each
+   Test for accesses into the same array through pointers with different
+   offsets each.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+#define A(p, off) ((p)[off] = __COUNTER__)
+
+extern int a4[4];
+
+
+NOIPA void p0_p1 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Warray-bounds" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  /* Since q points to a4 and -1 is a valid subscript, +3 must be invalid.
+     But the warning for each subscript is independent of prior subscripts
+     into the same object.  That should be improved.  */
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p1_p0 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p0 = a4 + 0;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Warray-bounds" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p1_p2 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Warray-bounds" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p2_p1 (int i)
+{
+  int *p2 = a4 + 2;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Warray-bounds" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p1_p3 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p3 = a4 + 3;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Warray-bounds" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p3_p1 (int i)
+{
+  int *p3 = a4 + 3;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Warray-bounds" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p1_p4 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p4 = a4 + 4;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Warray-bounds" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p4_p1 (int i)
+{
+  int *p4 = a4 + 4;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Warray-bounds" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p0_p1_p2 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i < 0 ? p1 : 0 < i ? p2 : p0;
+  A (q, -3);      // { dg-warning "-Warray-bounds" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p0_p1_p2_p3_p4 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *p3 = a4 + 3;
+  int *p4 = a4 + 4;
+  int *q = i < -1 ? p1 : i < 0 ? p2 : 1 < i ? p4 : 0 < i ? p3 : p0;
+  A (q, -5);      // { dg-warning "-Warray-bounds" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-92.c b/gcc/testsuite/gcc.dg/Warray-bounds-92.c
new file mode 100644
index 00000000000..8c8f5f7f459
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-92.c
@@ -0,0 +1,149 @@ 
+/* PR middle-end/103215 - bogus -Warray-bounds with two pointers with
+   different offsets each
+   Test for accesses into distinct arrays through pointers with different
+   offsets each.
+
+   If/when -Warray-bounds is enhanced to issue "maybe" kinds of warnings
+   some of the accesses here will trigger those and will need updating.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+#define A(p, off) ((p)[off] = __COUNTER__)
+
+extern int a4[4], a8[8];
+
+
+NOIPA void a4_p1_a8_p3 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p3 = a8 + 3;
+  int *q = i ? a4_p1 : a8_p3;
+  A (q, -4);      // { dg-warning "-Warray-bounds" }
+  /* Because -3 is a valid offset into a8 but not a4, q must point
+     to the former and so subscripts between -3 and +4 refer to its
+     elements.  */
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q,  1); A (q,  2); A (q,  3); A (q, 4);
+  A (q, 5);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  /* Both of the following are definitely out of bounds but the first isn't
+     diagnosed because the code conservatively merges the offsets into A4
+     and A8.  */
+  A (q, 7);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void a4_p1_a8_p5 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *q = i ? a4_p1 : a8_p5;
+  A (q, -6);     // { dg-warning "-Warray-bounds" }
+  /* Similarly to the above, because -5 is a valid offset into a8 but
+     not a4, q must point to the former and so subscripts between -5
+     and +2 refer to its elements.  */
+  A (q, -5); A (q, -4); A (q, -3); A (q, -2);
+  A (q, -1); A (q,  0); A (q,  1); A (q,  2);
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void a4_p1_a8_p7 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *q = i ? a4_p1 : a8_p7;
+  A (q, -8);     // { dg-warning "-Warray-bounds" }
+  A (q, -7); A (q, -6); A (q, -5); A (q, -4);
+  A (q, -3); A (q, -2); A (q, -1); A (q,  0);
+  /* Since -7 is valid, q must point to a8 and the last valid subscript
+     must be 0.  */
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void mp_1_a4_p1_a8_p7 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *p = i ? a4_p1 : a8_p7;
+  int *q = j ? p + 1 : p - 1;
+
+  A (q, -9);      // { dg-warning "-Warray-bounds" }
+
+  /* q points either to a8 + [6, 8] or a4 + [0, 2].  */
+  A (q, -8); A (q, -7); A (q, -6); A (q, -5);
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+
+  /* Since all the above are valid, q must point to a8 + 8. But as
+     mentioned above, the warning for each subscript is independent
+     of prior subscripts into the same object so the access below
+     aren't diagnosed.  */
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p5 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *p = i ? a4_p1 : a8_p5;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 6
+  A (q, -7);      // { dg-warning "-Warray-bounds" }
+  A (q, -6); A (q, -5); A (q, -4); A (q, -3);
+  A (q, -2); A (q, -1); A (q,  0); A (q,  1);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point to either a4 + [0, 2] or a8 + [4, 5].  */
+  A (q, 2);
+  /* q is now assumed to point tp a4.  Given that, only the first store
+     is valid.  */
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 5);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 6);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p4 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p4 = a8 + 4;
+  int *p = i ? a4_p1 : a8_p4;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 5
+  A (q, -6);      // { dg-warning "-Warray-bounds" }
+  A (q, -5);
+  A (q, -4);
+  A (q, -3);
+  A (q, -2);
+  A (q, -1);
+  A (q,  0);
+  A (q,  1);
+  A (q,  2);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point tp a4.  Given that, only the first store is valid.  */
+  A (q,  3);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  4);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  5);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  6);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  7);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  8);      // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
index b81186cfb94..e0a40788f0d 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
@@ -182,8 +182,8 @@  void memset_decl_2_off (void)
   int i2 = SR (2, INT_MAX);
 
   {
-    char a5[5];               // { dg-warning "at offset [1, 5] into destination object 'a5'
-    char a7[7];               // { dg-warning "at offset [2, 7] into destination object 'a7'
+    char a5[5];               // { dg-message "at offset \\\[1, 5] into destination object 'a5'" "note" }
+    char a7[7];               // { dg-message "at offset \\\[2, 7] into destination object 'a7'" "note" }
     char *p5_p1 = a5 + i1;
     char *p7_p2 = a7 + i2;
     char *p5_7 = cond1 ? p5_p1 : p7_p2;
@@ -193,7 +193,11 @@  void memset_decl_2_off (void)
     memset (p5_7, 0, 3);
     memset (p5_7, 0, 4);
     memset (p5_7, 0, 5);
-    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+    /* The warning code conservatively "merges" both the sizes and the offsets
+       into A5 and A7 and so only the second store below is diagnosed but not
+       the first.  See PR 103215.  The logic needs to be tightened up.  */
+    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " "pr??????" { xfail *-*-* } }
+    memset (p5_7, 0, 7);      // { dg-warning "memset' writing 7 bytes into a region of size 6 " }
   }
 
   int i3 = SR (3, INT_MAX);
@@ -208,7 +212,8 @@  void memset_decl_2_off (void)
     // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
     // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
     // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
-    // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message "at offset \\\[1, 9] into destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message ": destination object 'a9'" "pr??????" { xfail *-*-* } .-5 }
     char *p5_p2 = a5 + i2;    // 3 bytes left
     char *p9_p3 = a9 + i3;    // 6 bytes left
     char *p =
@@ -220,7 +225,8 @@  void memset_decl_2_off (void)
     memset (q, 0, 3);
     memset (q, 0, 4);
     memset (q, 0, 5);
-    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
 
     --q;                      // [3 - 6] bytes left
     memset (q, 0, 1);
@@ -229,7 +235,8 @@  void memset_decl_2_off (void)
     memset (q, 0, 4);
     memset (q, 0, 5);
     memset (q, 0, 6);
-    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
 
     --q;                      // [4 - 7] bytes left
     memset (q, 0, 1);
@@ -239,7 +246,8 @@  void memset_decl_2_off (void)
     memset (q, 0, 5);
     memset (q, 0, 6);
     memset (q, 0, 7);
-    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 9);         // { dg-warning "memset' writing 9 bytes into a region of size 8" }
 
     int m1_x = SR (-1, INT_MAX);
     int m2_x = SR (-2, INT_MAX);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
index c45a92d21e1..b6265e37c86 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
@@ -200,7 +200,11 @@  void memset_malloc_2_off (void)
     memset (p5_7, 0, 3);
     memset (p5_7, 0, 4);
     memset (p5_7, 0, 5);
-    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+    /* The warning code conservatively "merges" both the sizes and the offsets
+       into A5 and A7 and so only the second store below is diagnosed but not
+       the first.  See PR 103215.  The logic needs to be tightened up.  */
+    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " "pr??????" { xfail *-*-* } }
+    memset (p5_7, 0, 7);      // { dg-warning "memset' writing 7 bytes into a region of size 6 " }
   }
 
   int i3 = SR (3, INT_MAX);
@@ -215,7 +219,8 @@  void memset_malloc_2_off (void)
     // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
     // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
     // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
-    // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message "at offset \\\[1, 9] into destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message ": destination object 'a9'" "pr??????" { xfail *-*-* } .-5 }
     char *p5_p2 = a5 + i2;    // 3 bytes left
     char *p9_p3 = a9 + i3;    // 6 bytes left
     char *p =
@@ -227,7 +232,8 @@  void memset_malloc_2_off (void)
     memset (q, 0, 3);
     memset (q, 0, 4);
     memset (q, 0, 5);
-    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
 
     --q;                      // [3 - 6] bytes left
     memset (q, 0, 1);
@@ -236,7 +242,8 @@  void memset_malloc_2_off (void)
     memset (q, 0, 4);
     memset (q, 0, 5);
     memset (q, 0, 6);
-    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
 
     --q;                      // [4 - 7] bytes left
     memset (q, 0, 1);
@@ -246,7 +253,8 @@  void memset_malloc_2_off (void)
     memset (q, 0, 5);
     memset (q, 0, 6);
     memset (q, 0, 7);
-    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 9);         // { dg-warning "memset' writing 9 bytes into a region of size 8" }
 
     int m1_x = SR (-1, INT_MAX);
     int m2_x = SR (-2, INT_MAX);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c
new file mode 100644
index 00000000000..bc121928f1b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c
@@ -0,0 +1,147 @@ 
+/* PR middle-end/103215 - bogus -Warray-bounds with two pointers with
+   different offsets each
+   Test for accesses by a user-defined function into the same array
+   through pointers with different offsets each.  See Warray-bounds-91.c
+   for the corresponding test exercising -Warray-bounds for direct accesses.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+void sink (int[1]);
+#define A(p, off) sink (p + off)
+
+extern int a4[4];
+
+
+NOIPA void p0_p1 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  /* Since q points to a4 and -1 is a valid subscript, +3 must be invalid.
+     But the warning for each subscript is independent of prior subscripts
+     into the same object.  That should be improved.  */
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p1_p0 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p0 = a4 + 0;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p1_p2 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p2_p1 (int i)
+{
+  int *p2 = a4 + 2;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p1_p3 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p3 = a4 + 3;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p3_p1 (int i)
+{
+  int *p3 = a4 + 3;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p1_p4 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p4 = a4 + 4;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p4_p1 (int i)
+{
+  int *p4 = a4 + 4;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p0_p1_p2 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i < 0 ? p1 : 0 < i ? p2 : p0;
+  A (q, -3);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p0_p1_p2_p3_p4 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *p3 = a4 + 3;
+  int *p4 = a4 + 4;
+  int *q = i < -1 ? p1 : i < 0 ? p2 : 1 < i ? p4 : 0 < i ? p3 : p0;
+  A (q, -5);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-85.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-85.c
new file mode 100644
index 00000000000..ac61e0a0a64
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-85.c
@@ -0,0 +1,153 @@ 
+/* PR middle-end/103215 - bogus -Wstringop-overflow with two pointers with
+   different offsets each
+   Test for accesses into distinct arrays through pointers with different
+   offsets each.
+
+   If/when -Wstringop-overflow is enhanced to issue "maybe" kinds of
+   warnings some of the accesses here will trigger those and will need
+   updating.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+void sink (int[1]);
+#define A(p, off) sink (p + off)
+
+extern int a4[4], a8[8];
+
+
+
+
+NOIPA void a4_p1_a8_p3 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p3 = a8 + 3;
+  int *q = i ? a4_p1 : a8_p3;
+  A (q, -4);      // { dg-warning "-Wstringop-overflow" }
+  /* Because -3 is a valid offset into a8 but not a4, q must point
+     to the former and so subscripts between -3 and +4 refer to its
+     elements.  */
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q,  1); A (q,  2); A (q,  3); A (q, 4);
+  A (q, 5);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  /* Both of the following are definitely out of bounds but the first isn't
+     diagnosed because the code conservatively merges the offsets into A4
+     and A8.  */
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void a4_p1_a8_p5 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *q = i ? a4_p1 : a8_p5;
+  A (q, -6);     // { dg-warning "-Wstringop-overflow" }
+  /* Similarly to the above, because -5 is a valid offset into a8 but
+     not a4, q must point to the former and so subscripts between -5
+     and +2 refer to its elements.  */
+  A (q, -5); A (q, -4); A (q, -3); A (q, -2);
+  A (q, -1); A (q,  0); A (q,  1); A (q,  2);
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void a4_p1_a8_p7 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *q = i ? a4_p1 : a8_p7;
+  A (q, -8);     // { dg-warning "-Wstringop-overflow" }
+  A (q, -7); A (q, -6); A (q, -5); A (q, -4);
+  A (q, -3); A (q, -2); A (q, -1); A (q,  0);
+  /* Since -7 is valid, q must point to a8 and the last valid subscript
+     must be 0.  */
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void mp_1_a4_p1_a8_p7 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *p = i ? a4_p1 : a8_p7;
+  int *q = j ? p + 1 : p - 1;
+
+  A (q, -9);      // { dg-warning "-Wstringop-overflow" }
+
+  /* q points either to a8 + [6, 8] or a4 + [0, 2].  */
+  A (q, -8); A (q, -7); A (q, -6); A (q, -5);
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+
+  /* Since all the above are valid, q must point to a8 + 8. But as
+     mentioned above, the warning for each subscript is independent
+     of prior subscripts into the same object so the access below
+     aren't diagnosed.  */
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p5 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *p = i ? a4_p1 : a8_p5;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 6
+  A (q, -7);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -6); A (q, -5); A (q, -4); A (q, -3);
+  A (q, -2); A (q, -1); A (q,  0); A (q,  1);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point to either a4 + [0, 2] or a8 + [4, 5].  */
+  A (q, 2);
+  /* q is now assumed to point tp a4.  Given that, only the first store
+     is valid.  */
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 5);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 6);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p4 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p4 = a8 + 4;
+  int *p = i ? a4_p1 : a8_p4;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 5
+  A (q, -6);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -5);
+  A (q, -4);
+  A (q, -3);
+  A (q, -2);
+  A (q, -1);
+  A (q,  0);
+  A (q,  1);
+  A (q,  2);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point tp a4.  Given that, only the first store is valid.  */
+  A (q,  3);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  4);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  5);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  6);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  7);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  8);      // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 846e9300750..4ce70d036b7 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -143,17 +143,18 @@  T (v0 ? b[1] : "", bsz);
 T (v0 ? b[2] : "", bsz);
 T (v0 ? b[3] : "", bsz);
 
-/* 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.  */
+/* Warning for the calls below would be strictly correct even though
+   the strnlen calls are safe because the reads are bounded by
+   the length of the constant arguments.  Most of the calls are
+   not diagnosed anymore as a result of the fix for PR 103215.  */
 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[1], bsz + 1);
+T (v0 ? "" : b[2], bsz + 1);
+T (v0 ? "" : b[3], bsz + 1);
 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[1] : "", bsz + 1);
+T (v0 ? b[2] : "", bsz + 1);
+T (v0 ? b[3] : "", bsz + 1);
 
 T (v0 ? "" : b[i0], bsz);
 T (v0 ? "" : b[i1], bsz);
@@ -167,11 +168,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 "bound 6 exceeds source size 5" "pr86937" } */
+T (v0 ? "" : b[i3], bsz + 1);
 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 "bound 6 exceeds source size 5" "pr86937" } */
+T (v0 ? b[i3] : "", bsz + 1);
 
 T (v0 ? "1234" : b[3], bsz);
 T (v0 ? "1234" : b[i3], bsz);
@@ -183,14 +184,16 @@  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 "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 ? "1234" : b[3], bsz + 1);
+T (v0 ? "1234" : b[i3], bsz + 1);
+T (v0 ? b[3] : "1234", bsz + 1);
+T (v0 ? b[i3] : "1234", bsz + 1);
 
-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" } */
+/* That the following are not diagnosed is a bug/limitation resulting from
+   the fix for PR 103215.  */
+T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 exceeds source size 5" "pr??????" { xfail *-*-* } } */
+T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr??????" { xfail *-*-* } } */
+T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr??????" { xfail *-*-* } } */
 T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
 
 struct A { char a[5], b[5]; };