access_ref::add_offset() works hard to restore the property that
the lower bound of a range is less than or equal to its upper
bound.  But by capping the upper bound to at most PTRDIFF_MAX
without also considering the lower bound, it allows the latter
to exceed the value of the former, thus violating  the very
postcondition it aims to guarantee.

To correct this oversight I have committed the attached patch
as an obvious fix.  Tested on x86_64-linux.

Martin
PR middle-end/97556 - ICE on excessively large index into a multidimensional array

gcc/ChangeLog:

	PR middle-end/97556
	* builtins.c (access_ref::add_offset): Cap offset lower bound
	to at most the the upper bound.

gcc/testsuite/ChangeLog:

	PR middle-end/97556
	* gcc.dg/Warray-bounds-70.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 3a3eb5562df..da25343beb1 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -321,7 +321,13 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max)
       offrng[1] = maxoff;
       offset_int absmax = wi::abs (max);
       if (offrng[0] < absmax)
-	offrng[0] += min;
+	{
+	  offrng[0] += min;
+	  /* Cap the lower bound at the upper (set to MAXOFF above)
+	     to avoid inadvertently recreating an inverted range.  */
+	  if (offrng[1] < offrng[0])
+	    offrng[0] = offrng[1];
+	}
       else
 	offrng[0] = 0;
     }
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-70.c b/gcc/testsuite/gcc.dg/Warray-bounds-70.c
new file mode 100644
index 00000000000..087e255599c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-70.c
@@ -0,0 +1,18 @@
+/* PR middle-end/97556 - ICE on excessively large index into a multidimensional
+   array
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define SIZE_MAX __SIZE_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+char a[1][3];
+
+void f (int c)
+{
+  size_t i = c ? SIZE_MAX / 2 : SIZE_MAX;
+  a[i][0] = 0;                          // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// { dg-prune-output "\\\[-Wstringop-overflow=" }

Reply via email to