diff mbox series

[4/6] detect unterminated const arrays in sprintf calls (PR 86552)

Message ID 71628c87-1275-250b-e6c2-3ab284533074@gmail.com
State New
Headers show
Series improve handling of char arrays with missing nul (PR 86552, 86711, 86714) | expand

Commit Message

Martin Sebor Aug. 13, 2018, 9:28 p.m. UTC
The attached changes implement the detection of past-the-end reads
by the sprintf family of functions due to unterminated arguments to
%s directives.

Comments

Jeff Law Aug. 30, 2018, 10:55 p.m. UTC | #1
On 08/13/2018 03:28 PM, Martin Sebor wrote:
> The attached changes implement the detection of past-the-end reads
> by the sprintf family of functions due to unterminated arguments to
> %s directives.
> 
> gcc-86552-4.diff
> 
> 
> PR tree-optimization/86552 - missing warning for reading past the end of non-string arrays
> 
> gcc/ChangeLog:
> 
> 	* gimple-ssa-sprintf.c (struct fmtresult): Add new member and
> 	initialize it.
> 	(get_string_length): Detect unterminated arrays.
> 	(format_string): Same.
> 	(format_directive): Warn about unterminated arrays.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/warn-sprintf-no-nul.c: New test.
Largely the same state as #2 and #3.

I am getting a failure from the test though.  It looks like the sprintf
code is turning an offending sprintf call into a strcpy call and we end
up getting a warning from both.

> @@ -2988,6 +3002,18 @@ format_directive (const sprintf_dom_walker::call_info &info,
>  			  fmtres.range.min, fmtres.range.max);
>      }
>  
> +  if (!warned && fmtres.nonstr)
> +    {
> +      warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
> +			"%<%.*s%> directive argument is not a nul-terminated "
> +			"string",
> +			dirlen,
> +			target_to_host (hostdir, sizeof hostdir, dir.beg));
> +      if (warned && DECL_P (fmtres.nonstr))
> +	inform (DECL_SOURCE_LOCATION (fmtres.nonstr),
> +		"referenced argument declared here");
> +    }
> +
ISTM that returning false from this point should address the issue.
Essentially preventing the sprintf->strcpy transformation if the
directive argument is not NUL terminated.


I'll own this just like #2 and #3.

jeff
diff mbox series

Patch

PR tree-optimization/86552 - missing warning for reading past the end of non-string arrays

gcc/ChangeLog:

	* gimple-ssa-sprintf.c (struct fmtresult): Add new member and
	initialize it.
	(get_string_length): Detect unterminated arrays.
	(format_string): Same.
	(format_directive): Warn about unterminated arrays.

gcc/testsuite/ChangeLog:

	* gcc.dg/warn-sprintf-no-nul.c: New test.

diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index c652c55..95ab692 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -648,7 +648,7 @@  struct fmtresult
   /* Construct a FMTRESULT object with all counters initialized
      to MIN.  KNOWNRANGE is set when MIN is valid.  */
   fmtresult (unsigned HOST_WIDE_INT min = HOST_WIDE_INT_MAX)
-  : argmin (), argmax (),
+  : argmin (), argmax (), nonstr (),
     knownrange (min < HOST_WIDE_INT_MAX),
     nullp ()
   {
@@ -662,7 +662,7 @@  struct fmtresult
      KNOWNRANGE is set when both MIN and MAX are valid.   */
   fmtresult (unsigned HOST_WIDE_INT min, unsigned HOST_WIDE_INT max,
 	     unsigned HOST_WIDE_INT likely = HOST_WIDE_INT_MAX)
-  : argmin (), argmax (),
+  : argmin (), argmax (), nonstr (),
     knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
     nullp ()
   {
@@ -689,6 +689,10 @@  struct fmtresult
      results in on output for an argument in the range above.  */
   result_range range;
 
+  /* Non-nul when the argument of a string directive is not a nul
+     terminated string.  */
+  tree nonstr;
+
   /* True when the range above is obtained from a known value of
      a directive's argument or its bounds and not the result of
      heuristics that depend on warning levels.  */
@@ -2129,10 +2133,12 @@  get_string_length (tree str)
   if (!str)
     return fmtresult ();
 
-  if (tree slen = c_strlen (str, 1))
+  tree arr;
+  if (tree slen = c_strlen (str, 1, &arr))
     {
       /* Simply return the length of the string.  */
       fmtresult res (tree_to_shwi (slen));
+      res.nonstr = arr;
       return res;
     }
 
@@ -2140,9 +2146,11 @@  get_string_length (tree str)
      by STR.  Strings of unknown lengths are bounded by the sizes of
      arrays that subexpressions of STR may refer to.  Pointers that
      aren't known to point any such arrays result in LENRANGE[1] set
-     to SIZE_MAX.  */
+     to SIZE_MAX.  NONSTR is set to the declaration of the constant
+     array that is known not to be nul-terminated.  */
   tree lenrange[2];
-  bool flexarray = get_range_strlen (str, lenrange);
+  tree nonstr;
+  bool flexarray = get_range_strlen (str, lenrange, false, &nonstr);
 
   if (lenrange [0] || lenrange [1])
     {
@@ -2165,6 +2173,7 @@  get_string_length (tree str)
 	max = HOST_WIDE_INT_M1U;
 
       fmtresult res (min, max);
+      res.nonstr = nonstr;
 
       /* Set RES.KNOWNRANGE to true if and only if all strings referenced
 	 by STR are known to be bounded (though not necessarily by their
@@ -2422,6 +2431,11 @@  format_string (const directive &dir, tree arg, vr_values *)
       res.range.unlikely = res.range.max;
     }
 
+  /* If the argument isn't a nul-terminated string and the number
+     of bytes on output isn't bounded by precision, set NONSTR.  */
+  if (slen.nonstr && slen.range.min < (unsigned HOST_WIDE_INT)dir.prec[0])
+    res.nonstr = slen.nonstr;
+
   /* Bump up the byte counters if WIDTH is greater.  */
   return res.adjust_for_width_or_precision (dir.width);
 }
@@ -2988,6 +3002,18 @@  format_directive (const sprintf_dom_walker::call_info &info,
 			  fmtres.range.min, fmtres.range.max);
     }
 
+  if (!warned && fmtres.nonstr)
+    {
+      warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+			"%<%.*s%> directive argument is not a nul-terminated "
+			"string",
+			dirlen,
+			target_to_host (hostdir, sizeof hostdir, dir.beg));
+      if (warned && DECL_P (fmtres.nonstr))
+	inform (DECL_SOURCE_LOCATION (fmtres.nonstr),
+		"referenced argument declared here");
+    }
+
   if (warned && fmtres.range.min < fmtres.range.likely
       && fmtres.range.likely < fmtres.range.max)
     inform_n (info.fmtloc, fmtres.range.likely,
@@ -4033,6 +4059,8 @@  sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
   format_result res = format_result ();
 
   bool success = compute_format_length (info, &res);
+  if (res.warned)
+    gimple_set_no_warning (info.callstmt, true);
 
   /* When optimizing and the printf return value optimization is enabled,
      attempt to substitute the computed result for the return value of
diff --git a/gcc/testsuite/gcc.dg/warn-sprintf-no-nul.c b/gcc/testsuite/gcc.dg/warn-sprintf-no-nul.c
new file mode 100644
index 0000000..b331bb5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/warn-sprintf-no-nul.c
@@ -0,0 +1,90 @@ 
+/* PR tree-optimization/86552 - missing warning for reading past the end
+   of non-string arrays
+   Exercise non-string detection in sprintf.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds -Wall -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+typedef __WCHAR_TYPE__ wchar_t;
+
+extern int sprintf (char*, const char*, ...);
+
+extern char *dst;
+
+int i0 = 0;
+int i1 = 1;
+
+void sink (int, ...);
+
+#define CONCAT(a, b)   a ## b
+#define CAT(a, b)      CONCAT(a, b)
+
+#define T(fmt, ...)				\
+  sink (sprintf (dst, fmt, __VA_ARGS__))
+
+const char a[5] = "12345";    /* { dg-message "declared here" } */
+const char b[6] = "123456";   /* { dg-message "declared here" } */
+const char a2[][3] = {
+  "", "1", "12", "123", "123\000"   /* { dg-warning "initializer-string for array of chars is too long" } */
+};
+
+
+void test_narrow (void)
+{
+  /* Verify that precision suppresses the warning when it's less
+     than the size of the array.  */
+  T ("%.0s%.1s%.2s%.3s%.4s%.5s", a, a, a, a, a, a);
+
+  T ("%s", a);          /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
+  T ("%.6s", a);        /* { dg-warning ".%.6s. directive argument is not a nul-terminated string" } */
+
+  /* Exercise conditional expressions involving strings and non-strings.  */
+  const char *s0 = i0 < 0 ? a2[0] : a2[3];
+  T ("%s", s0);         /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
+  s0 = i0 < 0 ? "123456" : a2[4];
+  T ("%s", s0);         /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
+
+  const char *s1 = i0 < 0 ? a2[3] : a2[0];
+  T ("%s", s1);         /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
+
+  const char *s2 = i0 < 0 ? a2[3] : a2[4];
+  T ("%s", s2);         /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
+
+  s0 = i0 < 0 ? a : b;
+  T ("%.5s", s0);
+
+  /* Verify that the warning triggers even if precision prevents
+     reading past the end of one of the non-terminated arrays but
+     not the other.  */
+  T ("%.6s", s0);       /* { dg-warning ".%.6s. directive argument is not a nul-terminated string" } */
+
+  s0 = i0 < 0 ? b : a;
+  T ("%.7s", s0);       /* { dg-warning ".%.7s. directive argument is not a nul-terminated string" } */
+
+  /* Verify that at -Wformat-overflow=1 the lower bound of precision
+     given by a range is used to determine whether or not to warn.  */
+  int r = SR (4, 5);
+
+  T ("%.*s", r, a);
+  T ("%.*s", r, b);
+
+  r = SR (5, 6);
+  T ("%.*s", r, a);
+  T ("%.*s", r, b);
+
+  r = SR (6, 7);
+  T ("%.*s", r, a);     /* { dg-warning ".%.\\\*s. directive argument is not a nul-terminated string" } */
+  T ("%.*s", r, b);
+}
+
+
+const wchar_t wa[5] = L"12345";   /* { dg-message "declared here" } */
+
+void test_wide (void)
+{
+  T ("%.0ls%.1ls%.2ls%.3ls%.4ls%.5ls", wa, wa, wa, wa, wa, wa);
+
+  T ("%ls", wa);        /* { dg-warning ".%ls. directive argument is not a nul-terminated string" } */
+  T ("%.6ls", wa);      /* { dg-warning ".%.6ls. directive argument is not a nul-terminated string" } */
+}
diff --git a/gcc/testsuite/gcc.dg/warn-strcpy-no-nul.c b/gcc/testsuite/gcc.dg/warn-strcpy-no-nul.c
new file mode 100644
index 0000000..b06ec52
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/warn-strcpy-no-nul.c
@@ -0,0 +1,324 @@ 
+/* PR tree-optimization/86552 - missing warning for reading past the end
+   of non-string arrays
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+extern char* strcpy (char*, const char*);
+
+const char a[5] = "12345";   /* { dg-message "declared here" } */
+
+int v0 = 0;
+int v1 = 1;
+int v2 = 1;
+int v3 = 1;
+
+void sink (char*, ...);
+
+#define T(str) sink (strcpy (d, str))
+
+void test_one_dim_array (char *d)
+{
+  T (a);                /* { dg-warning "argument missing terminating nul" } */
+  T (&a[0]);            /* { dg-warning "nul" } */
+  T (&a[0] + 1);        /* { dg-warning "nul" } */
+  T (&a[1]);            /* { dg-warning "nul" } */
+
+  int i0 = 0;
+  int i1 = i0 + 1;
+
+  T (&a[i0]);           /* { dg-warning "nul" } */
+  T (&a[i0] + 1);       /* { dg-warning "nul" } */
+  T (&a[i1]);           /* { dg-warning "nul" } */
+
+  T (&a[v0]);           /* { dg-warning "nul" } */
+  T (&a[v0] + 1);       /* { dg-warning "nul" } */
+  T (&a[v0] + v1);      /* { dg-warning "nul" } */
+}
+
+const char b[][5] = { /* { dg-message "declared here" } */
+  "12", "123", "1234", "54321"
+};
+
+void test_two_dim_array (char *d)
+{
+  int i0 = 0;
+  int i1 = i0 + 1;
+  int i2 = i1 + 1;
+  int i3 = i2 + 1;
+
+  T (b[0]);
+  T (b[1]);
+  T (b[2]);
+  T (b[3]);             /* { dg-warning "nul" } */
+  T (b[i0]);
+  T (b[i1]);
+  T (b[i2]);
+  T (b[i3]);            /* { dg-warning "nul" } */
+  T (b[v0]);
+  T (b[v3]);
+
+  T (&b[2][1]);
+  T (&b[2][1] + 1);
+  T (&b[2][v0]);
+  T (&b[2][1] + v0);
+
+  T (&b[i2][i1]);
+  T (&b[i2][i1] + i1);
+  T (&b[i2][v0]);
+  T (&b[i2][i1] + v0);
+
+  T (&b[3][1]);         /* { dg-warning "nul" } */
+  T (&b[3][1] + 1);     /* { dg-warning "nul" } */
+  T (&b[3][v0]);        /* { dg-warning "nul" } */
+  T (&b[3][1] + v0);    /* { dg-warning "nul" } */
+  T (&b[3][v0] + v1);   /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (&b[i3][i1]);       /* { dg-warning "nul" } */
+  T (&b[i3][i1] + i1);  /* { dg-warning "nul" } */
+  T (&b[i3][v0]);       /* { dg-warning "nul" } */
+  T (&b[i3][i1] + v0);  /* { dg-warning "nul" } */
+  T (&b[i3][v0] + v1);  /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (v0 ? "" : b[0]);
+  T (v0 ? "" : b[1]);
+  T (v0 ? "" : b[2]);
+  T (v0 ? "" : b[3]);               /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? b[0] : "");
+  T (v0 ? b[1] : "");
+  T (v0 ? b[2] : "");
+  T (v0 ? b[3] : "");               /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (v0 ? "1234" : b[3]);           /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? b[3] : "1234");           /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (v0 ? a : b[3]);                /* { dg-warning "nul" } */
+  T (v0 ? b[0] : b[2]);
+  T (v0 ? b[2] : b[3]);             /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? b[3] : b[2]);             /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (v0 ? b[0] : &b[3][0] + 1);     /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? b[1] : &b[3][1] + v0);    /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  /* It's possible to detect the missing nul in the following two
+     expressions but GCC doesn't do it yet.  */
+  T (v0 ? &b[3][1] + v0 : b[2]);    /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? &b[3][v0] : &b[3][v1]);   /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+}
+
+struct A { char a[5], b[5]; };
+
+const struct A s = { "1234", "12345" };
+
+void test_struct_member (char *d)
+{
+  int i0 = 0;
+  int i1 = i0 + 1;
+
+  T (s.a);
+  T (&s.a[0]);
+  T (&s.a[0] + 1);
+  T (&s.a[0] + i0);
+  T (&s.a[1]);
+  T (&s.a[1] + 1);
+  T (&s.a[1] + i0);
+
+  T (&s.a[i0]);
+  T (&s.a[i0] + 1);
+  T (&s.a[i0] + v0);
+  T (&s.a[i1]);
+  T (&s.a[i1] + 1);
+  T (&s.a[i1] + v0);
+
+  T (s.a);
+  T (&s.a[0]);
+  T (&s.a[0] + 1);
+  T (&s.a[0] + v0);
+  T (&s.a[1]);
+  T (&s.a[1] + 1);
+  T (&s.a[1] + v0);
+
+  T (&s.a[i0]);
+  T (&s.a[i0] + 1);
+  T (&s.a[i0] + v0);
+  T (&s.a[i1]);
+  T (&s.a[i1] + 1);
+  T (&s.a[i1] + v0);
+
+  T (&s.a[v0]);
+  T (&s.a[v0] + 1);
+  T (&s.a[v0] + v0);
+  T (&s.a[v1]);
+  T (&s.a[v1] + 1);
+  T (&s.a[v1] + v0);
+
+  T (s.b);              /* { dg-warning "nul" } */
+  T (&s.b[0]);          /* { dg-warning "nul" } */
+  T (&s.b[0] + 1);      /* { dg-warning "nul" } */
+  T (&s.b[0] + i0);     /* { dg-warning "nul" } */
+  T (&s.b[1]);          /* { dg-warning "nul" } */
+  T (&s.b[1] + 1);      /* { dg-warning "nul" } */
+  T (&s.b[1] + i0);     /* { dg-warning "nul" } */
+
+  T (s.b);              /* { dg-warning "nul" } */
+  T (&s.b[0]);          /* { dg-warning "nul" } */
+  T (&s.b[0] + 1);      /* { dg-warning "nul" } */
+  T (&s.b[0] + v0);     /* { dg-warning "nul" } */
+  T (&s.b[1]);          /* { dg-warning "nul" } */
+  T (&s.b[1] + 1);      /* { dg-warning "nul" } */
+  T (&s.b[1] + v0);     /* { dg-warning "nul" } */
+
+  T (s.b);              /* { dg-warning "nul" } */
+  T (&s.b[v0]);         /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (&s.b[v0] + 1);     /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (&s.b[v0] + v0);    /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (&s.b[v1]);         /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (&s.b[v1] + 1);     /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (&s.b[v1] + v0);    /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+}
+
+struct B { struct A a[2]; };
+
+const struct B ba[] = {
+  { { { "123", "12345" }, { "12345", "123" } } },
+  { { { "12345", "123" }, { "123", "12345" } } },
+  { { { "1", "12" },      { "123", "1234" } } },
+  { { { "123", "1234" },  { "12345", "12" } } }
+};
+
+void test_array_of_structs (char *d)
+{
+  T (ba[0].a[0].a);
+  T (&ba[0].a[0].a[0]);
+  T (&ba[0].a[0].a[0] + 1);
+  T (&ba[0].a[0].a[0] + v0);
+  T (&ba[0].a[0].a[1]);
+  T (&ba[0].a[0].a[1] + 1);
+  T (&ba[0].a[0].a[1] + v0);
+
+  T (ba[0].a[0].b);           /* { dg-warning "nul" } */
+  T (&ba[0].a[0].b[0]);       /* { dg-warning "nul" } */
+  T (&ba[0].a[0].b[0] + 1);   /* { dg-warning "nul" } */
+  T (&ba[0].a[0].b[0] + v0);  /* { dg-warning "nul" } */
+  T (&ba[0].a[0].b[1]);       /* { dg-warning "nul" } */
+  T (&ba[0].a[0].b[1] + 1);   /* { dg-warning "nul" } */
+  T (&ba[0].a[0].b[1] + v0);  /* { dg-warning "nul" } */
+
+  T (ba[0].a[1].a);           /* { dg-warning "nul" } */
+  T (&ba[0].a[1].a[0]);       /* { dg-warning "nul" } */
+  T (&ba[0].a[1].a[0] + 1);   /* { dg-warning "nul" } */
+  T (&ba[0].a[1].a[0] + v0);  /* { dg-warning "nul" } */
+  T (&ba[0].a[1].a[1]);       /* { dg-warning "nul" } */
+  T (&ba[0].a[1].a[1] + 1);   /* { dg-warning "nul" } */
+  T (&ba[0].a[1].a[1] + v0);  /* { dg-warning "nul" } */
+
+  T (ba[0].a[1].b);
+  T (&ba[0].a[1].b[0]);
+  T (&ba[0].a[1].b[0] + 1);
+  T (&ba[0].a[1].b[0] + v0);
+  T (&ba[0].a[1].b[1]);
+  T (&ba[0].a[1].b[1] + 1);
+  T (&ba[0].a[1].b[1] + v0);
+
+
+  T (ba[1].a[0].a);           /* { dg-warning "nul" } */
+  T (&ba[1].a[0].a[0]);       /* { dg-warning "nul" } */
+  T (&ba[1].a[0].a[0] + 1);   /* { dg-warning "nul" } */
+  T (&ba[1].a[0].a[0] + v0);  /* { dg-warning "nul" } */
+  T (&ba[1].a[0].a[1]);       /* { dg-warning "nul" } */
+  T (&ba[1].a[0].a[1] + 1);   /* { dg-warning "nul" } */
+  T (&ba[1].a[0].a[1] + v0);  /* { dg-warning "nul" } */
+
+  T (ba[1].a[0].b);
+  T (&ba[1].a[0].b[0]);
+  T (&ba[1].a[0].b[0] + 1);
+  T (&ba[1].a[0].b[0] + v0);
+  T (&ba[1].a[0].b[1]);
+  T (&ba[1].a[0].b[1] + 1);
+  T (&ba[1].a[0].b[1] + v0);
+
+  T (ba[1].a[1].a);
+  T (&ba[1].a[1].a[0]);
+  T (&ba[1].a[1].a[0] + 1);
+  T (&ba[1].a[1].a[0] + v0);
+  T (&ba[1].a[1].a[1]);
+  T (&ba[1].a[1].a[1] + 1);
+  T (&ba[1].a[1].a[1] + v0);
+
+  T (ba[1].a[1].b);           /* { dg-warning "nul" } */
+  T (&ba[1].a[1].b[0]);       /* { dg-warning "nul" } */
+  T (&ba[1].a[1].b[0] + 1);   /* { dg-warning "nul" } */
+  T (&ba[1].a[1].b[0] + v0);  /* { dg-warning "nul" } */
+  T (&ba[1].a[1].b[1]);       /* { dg-warning "nul" } */
+  T (&ba[1].a[1].b[1] + 1);   /* { dg-warning "nul" } */
+  T (&ba[1].a[1].b[1] + v0);  /* { dg-warning "nul" } */
+
+
+  T (ba[2].a[0].a);
+  T (&ba[2].a[0].a[0]);
+  T (&ba[2].a[0].a[0] + 1);
+  T (&ba[2].a[0].a[0] + v0);
+  T (&ba[2].a[0].a[1]);
+  T (&ba[2].a[0].a[1] + 1);
+  T (&ba[2].a[0].a[1] + v0);
+
+  T (ba[2].a[0].b);
+  T (&ba[2].a[0].b[0]);
+  T (&ba[2].a[0].b[0] + 1);
+  T (&ba[2].a[0].b[0] + v0);
+  T (&ba[2].a[0].b[1]);
+  T (&ba[2].a[0].b[1] + 1);
+  T (&ba[2].a[0].b[1] + v0);
+
+  T (ba[2].a[1].a);
+  T (&ba[2].a[1].a[0]);
+  T (&ba[2].a[1].a[0] + 1);
+  T (&ba[2].a[1].a[0] + v0);
+  T (&ba[2].a[1].a[1]);
+  T (&ba[2].a[1].a[1] + 1);
+  T (&ba[2].a[1].a[1] + v0);
+
+
+  T (ba[3].a[0].a);
+  T (&ba[3].a[0].a[0]);
+  T (&ba[3].a[0].a[0] + 1);
+  T (&ba[3].a[0].a[0] + v0);
+  T (&ba[3].a[0].a[1]);
+  T (&ba[3].a[0].a[1] + 1);
+  T (&ba[3].a[0].a[1] + v0);
+
+  T (ba[3].a[0].b);
+  T (&ba[3].a[0].b[0]);
+  T (&ba[3].a[0].b[0] + 1);
+  T (&ba[3].a[0].b[0] + v0);
+  T (&ba[3].a[0].b[1]);
+  T (&ba[3].a[0].b[1] + 1);
+  T (&ba[3].a[0].b[1] + v0);
+
+  T (ba[3].a[1].a);           /* { dg-warning "nul" } */
+  T (&ba[3].a[1].a[0]);	      /* { dg-warning "nul" } */
+  T (&ba[3].a[1].a[0] + 1);   /* { dg-warning "nul" } */
+  T (&ba[3].a[1].a[0] + v0);  /* { dg-warning "nul" } */
+  T (&ba[3].a[1].a[1]);	      /* { dg-warning "nul" } */
+  T (&ba[3].a[1].a[1] + 1);   /* { dg-warning "nul" } */
+  T (&ba[3].a[1].a[1] + v0);  /* { dg-warning "nul" } */
+
+  T (ba[3].a[1].b);
+  T (&ba[3].a[1].b[0]);	
+  T (&ba[3].a[1].b[0] + 1);
+  T (&ba[3].a[1].b[0] + v0);
+  T (&ba[3].a[1].b[1]);	
+  T (&ba[3].a[1].b[1] + 1);
+  T (&ba[3].a[1].b[1] + v0);
+
+
+  T (v0 ? ba[0].a[0].a : ba[0].a[0].b);           /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? ba[0].a[0].a : ba[0].a[0].b);           /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (v0 ? &ba[0].a[0].a[0] : &ba[3].a[1].a[0]);   /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+  T (v0 ? &ba[3].a[1].a[1] :  ba[0].a[0].a);      /* { dg-warning "nul" "bug ???" { xfail *-*-* } } */
+
+  T (v0 ? ba[0].a[0].a : ba[0].a[1].b);
+  T (v0 ? ba[0].a[1].b : ba[0].a[0].a);
+}
+
+/* { dg-prune-output " reading \[1-9\]\[0-9\]? bytes from a region " } */