]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX in glibc...
authormsebor <msebor@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 3 Feb 2017 02:18:59 +0000 (02:18 +0000)
committermsebor <msebor@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 3 Feb 2017 02:18:59 +0000 (02:18 +0000)
gcc/testsuite/ChangeLog:

PR middle-end/79275
* gcc.dg/tree-ssa/builtin-sprintf-warn-11.c: New test.
* gcc.dg/tree-ssa/pr79275.c: New test.

gcc/ChangeLog:

PR middle-end/79275
* gimple-ssa-sprintf.c (get_string_length): Set lower bound to zero.
(format_string): Tighten up the range of output for non-constant
strings and correct the expected range for wide non-constant strings.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@245142 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/gimple-ssa-sprintf.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/pr79275.c [new file with mode: 0644]

index c7ac7ee9cebd89f96b56898426097500f4ddcbaf..fe36a9e0f7b7009bc965ff7c0afb3e1775014f7e 100644 (file)
@@ -1,3 +1,10 @@
+2017-02-02  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/79275
+       * gimple-ssa-sprintf.c (get_string_length): Set lower bound to zero.
+       (format_string): Tighten up the range of output for non-constant
+       strings and correct the expected range for wide non-constant strings.
+
 2017-02-02  Martin Sebor  <msebor@redhat.com>
 
        * doc/invoke.texi (-maccumulate-args): Fix bad grammar.
index 11f41741f95f38d389226590f08111f548974649..9e099f0a1330de9e8db1c195374cb48e691310bc 100644 (file)
@@ -1832,10 +1832,11 @@ get_string_length (tree str)
        }
       else
        {
-         /* When the upper bound is unknown (as assumed to be excessive)
+         /* When the upper bound is unknown (it can be zero or excessive)
             set the likely length to the greater of 1 and the length of
-            the shortest string.  */
+            the shortest string and reset the lower bound to zero.  */
          res.range.likely = res.range.min ? res.range.min : warn_level > 1;
+         res.range.min = 0;
        }
 
       res.range.unlikely = res.range.max;
@@ -1986,43 +1987,89 @@ format_string (const directive &dir, tree arg)
     }
   else
     {
-      /* For a '%s' and '%ls' directive with a non-constant string,
-        the minimum number of characters is the greater of WIDTH
-        and either 0 in mode 1 or the smaller of PRECISION and 1
-        in mode 2, and the maximum is PRECISION or -1 to disable
-        tracking.  */
+      /* For a '%s' and '%ls' directive with a non-constant string (either
+        one of a number of strings of known length or an unknown string)
+        the minimum number of characters is lesser of PRECISION[0] and
+        the length of the shortest known string or zero, and the maximum
+        is the lessser of the length of the longest known string or
+        PTRDIFF_MAX and PRECISION[1].  The likely length is either
+        the minimum at level 1 and the greater of the minimum and 1
+        at level 2.  This result is adjust upward for width (if it's
+        specified).  */
+
+      if (dir.modifier == FMT_LEN_l)
+       {
+         /* A wide character converts to as few as zero bytes.  */
+         slen.range.min = 0;
+         if (slen.range.max < target_int_max ())
+           slen.range.max *= target_mb_len_max ();
+
+         if (slen.range.likely < target_int_max ())
+           slen.range.likely *= 2;
+
+         if (slen.range.likely < target_int_max ())
+           slen.range.unlikely *= target_mb_len_max ();
+       }
+
+      res.range = slen.range;
 
       if (dir.prec[0] >= 0)
        {
+         /* Adjust the minimum to zero if the string length is unknown,
+            or at most the lower bound of the precision otherwise.  */
          if (slen.range.min >= target_int_max ())
-           slen.range.min = 0;
+           res.range.min = 0;
          else if ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.min)
-           {
-             slen.range.min = dir.prec[0];
-             slen.range.likely = slen.range.min;
-           }
+           res.range.min = dir.prec[0];
 
+         /* Make both maxima no greater than the upper bound of precision.  */
          if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max
              || slen.range.max >= target_int_max ())
            {
-             slen.range.max = dir.prec[1];
-             slen.range.likely = slen.range.max;
+             res.range.max = dir.prec[1];
+             res.range.unlikely = dir.prec[1];
            }
+
+         /* If precision is constant, set the likely counter to the lesser
+            of it and the maximum string length.  Otherwise, if the lower
+            bound of precision is greater than zero, set the likely counter
+            to the minimum.  Otherwise set it to zero or one based on
+            the warning level.  */
+         if (dir.prec[0] == dir.prec[1])
+           res.range.likely
+             = ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.max
+                ? dir.prec[0] : slen.range.max);
+         else if (dir.prec[0] > 0)
+           res.range.likely = res.range.min;
+         else
+           res.range.likely = warn_level > 1;
+       }
+      else if (dir.prec[1] >= 0)
+       {
+         res.range.min = 0;
+         if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
+           res.range.max = dir.prec[1];
+         res.range.likely = dir.prec[1] ? warn_level > 1 : 0;
        }
       else if (slen.range.min >= target_int_max ())
        {
-         slen.range.min = 0;
-         slen.range.max = HOST_WIDE_INT_MAX;
-         /* At level one strings of unknown length are assumed to be
+         res.range.min = 0;
+         res.range.max = HOST_WIDE_INT_MAX;
+         /* At level 1 strings of unknown length are assumed to be
             empty, while at level 1 they are assumed to be one byte
             long.  */
-         slen.range.likely = warn_level > 1;
+         res.range.likely = warn_level > 1;
+       }
+      else
+       {
+         /* A string of unknown length unconstrained by precision is
+            assumed to be empty at level 1 and just one character long
+            at higher levels.  */
+         if (res.range.likely >= target_int_max ())
+           res.range.likely = warn_level > 1;
        }
 
-      slen.range.unlikely = slen.range.max;
-
-      res.range = slen.range;
-      res.knownrange = slen.knownrange;
+      res.range.unlikely = res.range.max;
     }
 
   /* Bump up the byte counters if WIDTH is greater.  */
index 07ff543e6a158fc7040db544c15709a447ae320b..1289892dadaf4da122ab9b9811c02877b9ce6d79 100644 (file)
@@ -1,3 +1,9 @@
+2017-02-02  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/79275
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-11.c: New test.
+       * gcc.dg/tree-ssa/pr79275.c: New test.
+
 2017-02-02  Aaron Sawdey  <acsawdey@linux.vnet.ibm.com>
 
         * gcc.dg/sms-8.c: Update options for powerpc*-*-*.
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c
new file mode 100644 (file)
index 0000000..b714c95
--- /dev/null
@@ -0,0 +1,323 @@
+/* PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX
+   in glibc sysdeps/posix/tempname.c
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__  size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+/* When debugging, define LINE to the line number of the test case to exercise
+   and avoid exercising any of the others.  The buffer and objsize macros
+   below make use of LINE to avoid warnings for other lines.  */
+#ifndef LINE
+# define LINE 0
+#endif
+
+extern int int_value (void);
+extern size_t size_value (void);
+
+int int_range (int min, int max)
+{
+  int n = int_value ();
+  return n < min || max < n ? min : n;
+}
+
+size_t size_range (size_t min, size_t max)
+{
+  size_t n = size_value ();
+  return n < min || max < n ? min : n;
+}
+
+void sink (char*, char*);
+
+int dummy_sprintf (char*, const char*, ...);
+
+char buffer [256];
+extern char *ptr;
+
+const char s0[] = "";
+const char s1[] = "1";
+const char s2[] = "12";
+const char s3[] = "123";
+const char s4[] = "1234";
+const char s5[] = "12345";
+const char s6[] = "123456";
+const char s7[] = "1234567";
+const char s8[] = "12345678";
+const char s9[] = "123456789";
+extern const char sx[];
+extern const char sy[];
+
+/* Wide string literals outside the ASCII range to avoid assumptions
+   about the number of narrow characters they might convert to beyond
+   up to 6 bytes each (the maximum for UTF-8 not exceeded by any known
+   encoding).  */
+const wchar_t ws0[] = L"";
+const wchar_t ws1[] = L"\u1111";
+const wchar_t ws2[] = L"\u1111\u2222";
+const wchar_t ws3[] = L"\u1111\u2222\u3333";
+const wchar_t ws4[] = L"\u1111\u2222\u3333\u4444";
+const wchar_t ws5[] = L"\u1111\u2222\u3333\u4444\u5555";
+const wchar_t ws6[] = L"\u1111\u2222\u3333\u4444\u5555\u6666";
+const wchar_t ws7[] = L"\u1111\u2222\u3333\u4444\u5555\u6666\u7777";
+const wchar_t ws8[] =
+  L"\u1111\u2222\u3333\u4444\u5555\u6666\u7777\u8888";
+const wchar_t ws9[] =
+  L"\u1111\u2222\u3333\u4444\u5555\u6666\u7777\u8888\u9999";
+extern const wchar_t wsx[];
+extern const wchar_t wsy[];
+
+static const int imin = INT_MIN;
+static const int imax = INT_MAX;
+
+/* Evaluate to an array of SIZE characters when non-negative, or to
+   a pointer to an unknown object otherwise.  */
+#define buffer(size)                                   \
+  ((0 <= size) ? buffer + sizeof buffer - (size) : ptr)
+
+/* Helper to expand function to either __builtin_f or dummy_f to
+   make debugging GCC easy.  */
+#define FUNC(f)                                                        \
+  ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f)
+
+/* Macro to verify that calls to __builtin_sprintf (i.e., with no size
+   argument) issue diagnostics by correctly determining the size of
+   the destination buffer.  */
+#define T(size, ...)                                           \
+  (FUNC (sprintf) (buffer (size),  __VA_ARGS__),               \
+   sink (buffer, ptr))
+
+/* Return a value in the range [MIN, MAX].  */
+#define IR(min, max)  int_range (min, max)
+
+/* Return a string whose length is in the range [MIN, MAX] where
+   both MIN and MAX must be digits in the range [0, 9].  */
+#define SR(min, max)  (int_value () < 0 ? s##min : s##max)
+
+/* Return a wide string whose length is in the range [MIN, MAX] where
+   both MIN and MAX must be digits in the range [0, 9].  */
+#define WR(min, max)  (int_value () < 0 ? ws##min : ws##max)
+
+void test_narrow_string_with_precision (void)
+{
+  T (-1, "%.*s", IR ( 0,  1), SR (0, 1));
+  T (-1, "%.*s", IR ( 0,  1), SR (0, 2));
+  T (-1, "%.*s", IR ( 0,  1), SR (0, 3));
+  T (-1, "%.*s", IR ( 0,  1), SR (0, 4));
+  T (-1, "%.*s", IR ( 0,  1), SR (0, 9));
+  T (-1, "%.*s", IR ( 0,  2), SR (0, 9));
+  T (-1, "%.*s", IR ( 0,  3), SR (0, 9));
+  T (-1, "%.*s", IR ( 0,  4), SR (0, 9));
+  T (-1, "%.*s", IR ( 0,  9), SR (0, 9));
+  T (-1, "%.*s", IR ( 0, 99), SR (0, 9));
+  T (-1, "%.*s", IR ( 0, 99), SR (0, x));
+  T (-1, "%.*s", IR ( 0, 99), SR (1, x));
+  T (-1, "%.*s", IR ( 0, 99), SR (x, 1));
+  T (-1, "%.*s", IR ( 0, 99), SR (x, 9));
+
+  T (-1, "%.*s", IR (imax / 3, imax / 2), SR (x, y));
+
+  /* Non-constant zero length string.  */
+  T ( 0, "%.*s", IR (imin, -1), SR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (imin,  0), SR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (-1,    0), SR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (-1,    1), SR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (-1,   99), SR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+
+  /* String with length between 0 and 1 character.  */
+  T ( 0, "%.*s", IR (imin, -1), SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR (imin, 0),  SR (0, 1));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (-2, -1),   SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR (-2,  0),   SR (0, 1));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR ( 0,  1),   SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 0,  2),   SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 0, 99),   SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 0, imax), SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 1, imax), SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 9, imax), SR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+
+  /* String with length between 2 and 3 characters.  */
+  T ( 0, "%.*s", IR (imin, -1), SR (2, 3));   /* { dg-warning "writing between 2 and 3 bytes" } */
+  T ( 0, "%.*s", IR (imin, 0),  SR (2, 3));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (-2, -1),   SR (2, 3));   /* { dg-warning "writing between 2 and 3 bytes" } */
+  T ( 0, "%.*s", IR (-2,  0),   SR (2, 3));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (-2,  1),   SR (2, 3));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 0,  1),   SR (2, 3));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 0,  2),   SR (2, 3));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*s", IR ( 0, 99),   SR (2, 3));   /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%.*s", IR ( 0, imax), SR (2, 3));   /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%.*s", IR ( 1, 99),   SR (2, 3));   /* { dg-warning "writing between 1 and 3 bytes" } */
+  T ( 0, "%.*s", IR ( 9, 99),   SR (2, 3));   /* { dg-warning "writing between 2 and 3 bytes" } */
+
+  T ( 0, "%.*s", IR ( 0,  1),   SR (0, 9));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*s", IR ( 0,  2),   SR (0, 9));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*s", IR ( 0,  9),   SR (0, 9));   /* { dg-warning "writing up to 9 bytes" } */
+  T ( 0, "%.*s", IR ( 0, 79),   SR (0, 9));   /* { dg-warning "writing up to 9 bytes" } */
+  T ( 0, "%.*s", IR ( 1,  2),   SR (0, 9));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*s", IR ( 2,  3),   SR (0, 9));   /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%.*s", IR ( 7, 13),   SR (0, 9));   /* { dg-warning "writing up to 9 bytes" } */
+
+  /* String between N and unknown number of characters long.  */
+  T ( 0, "%.*s", IR (imin, -1), SR (0, x));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*s", IR (imin, -1), SR (1, x));   /* { dg-warning "writing likely 1 or more bytes" } */
+  T ( 1, "%.*s", IR (imin, -1), SR (1, x));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*s", IR (imin, -1), SR (8, x));   /* { dg-warning "writing likely 8 or more bytes" } */
+  T ( 1, "%.*s", IR (imin, -1), SR (x, 9));   /* { dg-warning "writing likely 9 or more bytes" } */
+
+  /* Unknown strings.  */
+  T ( 1, "%.*s", IR (imin, -1), SR (x, y));
+  T ( 1, "%.*s", IR (imin,  0), SR (x, y));
+  T ( 1, "%.*s", IR ( -99,  1), SR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*s", IR (  -2,  2), SR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*s", IR (  -1, 99), SR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*s", IR (   0, 99), SR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*s", IR (   1, 99), SR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*s", IR (   9, 99), SR (x, y));   /* { dg-warning "may write a terminating nul" } */
+}
+
+void test_narrow_string_with_width_and_precision (void)
+{
+  T (-1, "%*.*s", IR ( 0,  1), IR ( 0,  1), SR (0, 1));
+  T (-1, "%*.*s", IR ( 0,  1), IR ( 0,  1), SR (0, 2));
+  T (-1, "%*.*s", IR ( 0,  1), IR ( 0,  1), SR (0, 3));
+  T (-1, "%*.*s", IR ( 0,  1), IR ( 0,  1), SR (0, 4));
+  T (-1, "%*.*s", IR ( 0,  1), IR ( 0,  1), SR (0, 9));
+  T (-1, "%*.*s", IR ( 0,  2), IR ( 0,  2), SR (0, 9));
+  T (-1, "%*.*s", IR ( 0,  3), IR ( 0,  3), SR (0, 9));
+  T (-1, "%*.*s", IR ( 0,  4), IR ( 0,  4), SR (0, 9));
+  T (-1, "%*.*s", IR ( 0,  9), IR ( 0,  9), SR (0, 9));
+  T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (0, 9));
+  T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (0, x));
+  T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (1, x));
+  T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (x, 1));
+  T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (x, 9));
+  T (-1, "%*.*s", IR (12, 34), IR (45, 67), SR (x, 9));
+  T (-1, "%*.*s", IR (12, 34), IR (45, 67), SR (x, y));
+
+  T (-1, "%*.*s", IR (imax / 5, imax / 4), IR (imax / 3, imax / 2), SR (x, y));
+
+  T (-1, "%*.*s %*.*s",
+     IR (imax / 9, imax / 8), IR (imax / 7, imax / 6), SR (x, y),
+     IR (imax / 5, imax / 4), IR (imax / 3, imax / 2), SR (x, y));
+
+  /* The two directives below combined convert to [INT_MAX, INT_MAX + 1].
+     Since the lower end of the range doesn't exceed INT_MAX no warning
+     is expected.  */
+  T (-1, "%*.*s%*.*s",
+     IR (imax - 5, imax - 3), IR (1, 2), SR (x, y),
+     IR (       5,        6), IR (3, 4), SR (x, y));
+
+  /* The three directives below (the two %s plus the space in between)
+     combined convert to [INT_MAX + 1, INT_MAX + 2].  Since the lower
+     end of the range exceeds INT_MAX a warning is expected.  */
+  T (-1, "%*.*s %*.*s",                                     /* { dg-warning "INT_MAX" } */
+     IR (imax - 5, imax - 3), IR (1, 2), SR (x, y),
+     IR (       5,        6), IR (3, 4), SR (x, y));
+
+   /* Non-constant zero length string.  */
+  T ( 0, "%*.*s", IR ( 0, 1), IR (imin, -1), SR (0, 0));    /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%*.*s", IR ( 0, 2), IR (imin, -1), SR (0, 0));    /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (imin, -1), SR (0, 0));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  1), SR (0, 0));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  1), SR (0, 1));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  2), SR (0, 1));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  3), SR (0, 1));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  1), SR (3, 5));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  2), SR (3, 5));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  3), SR (3, 5));    /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  4), SR (3, 5));    /* { dg-warning "writing up to 4 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  5), SR (3, 5));    /* { dg-warning "writing up to 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 0, 3), IR (   0,  6), SR (3, 5));    /* { dg-warning "writing up to 5 bytes" } */
+
+  T ( 0, "%*.*s", IR ( 1, 2), IR (   0,  1), SR (3, 5));    /* { dg-warning "writing between 1 and 2 bytes" } */
+  T ( 0, "%*.*s", IR ( 1, 2), IR (   0,  2), SR (3, 5));    /* { dg-warning "writing between 1 and 2 bytes" } */
+  T ( 0, "%*.*s", IR ( 1, 2), IR (   0,  3), SR (3, 5));    /* { dg-warning "writing between 1 and 3 bytes" } */
+  T ( 0, "%*.*s", IR ( 1, 2), IR (   0,  4), SR (3, 5));    /* { dg-warning "writing between 1 and 4 bytes" } */
+  T ( 0, "%*.*s", IR ( 1, 2), IR (   0,  5), SR (3, 5));    /* { dg-warning "writing between 1 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 1, 2), IR (   0,  6), SR (3, 5));    /* { dg-warning "writing between 1 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 2, 3), IR (   0,  6), SR (3, 5));    /* { dg-warning "writing between 2 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 2, 3), IR (   1,  6), SR (3, 5));    /* { dg-warning "writing between 2 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 2, 3), IR (   2,  6), SR (3, 5));    /* { dg-warning "writing between 2 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 2, 3), IR (   3,  6), SR (3, 5));    /* { dg-warning "writing between 3 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 2, 3), IR (   4,  6), SR (3, 5));    /* { dg-warning "writing between 3 and 5 bytes" } */
+  T ( 0, "%*.*s", IR ( 2, 3), IR (   5,  6), SR (3, 5));    /* { dg-warning "writing between 3 and 5 bytes" } */
+}
+
+void test_wide_string (void)
+{
+  T (-1, "%.*ls", IR ( 0,  1), WR (0, 1));
+  T (-1, "%.*ls", IR ( 0,  1), WR (0, 2));
+  T (-1, "%.*ls", IR ( 0,  1), WR (0, 3));
+  T (-1, "%.*ls", IR ( 0,  1), WR (0, 4));
+  T (-1, "%.*ls", IR ( 0,  1), WR (0, 9));
+  T (-1, "%.*ls", IR ( 0,  2), WR (0, 9));
+  T (-1, "%.*ls", IR ( 0,  3), WR (0, 9));
+  T (-1, "%.*ls", IR ( 0,  4), WR (0, 9));
+  T (-1, "%.*ls", IR ( 0,  9), WR (0, 9));
+  T (-1, "%.*ls", IR ( 0, 99), WR (0, 9));
+  T (-1, "%.*ls", IR ( 0, 99), WR (0, x));
+  T (-1, "%.*ls", IR ( 0, 99), WR (1, x));
+  T (-1, "%.*ls", IR ( 0, 99), WR (x, 1));
+  T (-1, "%.*ls", IR ( 0, 99), WR (x, 9));
+
+   /* Non-constant zero length string.  */
+  T ( 0, "%.*ls", IR (imin, -1), WR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (imin,  0), WR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (-1,    0), WR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (-1,    1), WR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (-1,   99), WR (0, 0));   /* { dg-warning "writing a terminating nul" } */
+
+  /* String with length between 0 and 1 character.  */
+  T ( 0, "%.*ls", IR (imin, -1), WR (0, 1));   /* { dg-warning "writing up to 6 bytes" } */
+  T ( 0, "%.*ls", IR (imin, 0),  WR (0, 1));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (-2, -1),   WR (0, 1));   /* { dg-warning "writing up to 6 bytes" } */
+  T ( 0, "%.*ls", IR (-2,  0),   WR (0, 1));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR ( 0,  1),   WR (0, 1));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*ls", IR ( 0,  2),   WR (0, 1));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*ls", IR ( 0, 99),   WR (0, 1));   /* { dg-warning "writing up to 6 bytes" } */
+  T ( 0, "%.*ls", IR ( 0, imax), WR (0, 1));   /* { dg-warning "writing up to 6 bytes" } */
+  T ( 0, "%.*ls", IR ( 1, imax), WR (0, 1));   /* { dg-warning "writing up to 6 bytes" } */
+  T ( 0, "%.*ls", IR ( 9, imax), WR (0, 1));   /* { dg-warning "writing up to 6 bytes" } */
+
+  /* String with length between 2 and 3 characters.  */
+  T ( 0, "%.*ls", IR (imin, -1), WR (2, 3));   /* { dg-warning "writing up to 18 bytes" } */
+  T ( 0, "%.*ls", IR (imin, 0),  WR (2, 3));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (-2, -1),   WR (2, 3));   /* { dg-warning "writing up to 18 bytes" } */
+  T ( 0, "%.*ls", IR (-2,  0),   WR (2, 3));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (-2,  1),   WR (2, 3));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*ls", IR ( 0,  1),   WR (2, 3));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*ls", IR ( 0,  2),   WR (2, 3));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*ls", IR ( 0, 99),   WR (2, 3));   /* { dg-warning "writing up to 18 bytes" } */
+  T ( 0, "%.*ls", IR ( 0, imax), WR (2, 3));   /* { dg-warning "writing up to 18 bytes" } */
+  T ( 0, "%.*ls", IR ( 1, 99),   WR (2, 3));   /* { dg-warning "writing up to 18 bytes" } */
+  T ( 0, "%.*ls", IR ( 9, 99),   WR (2, 3));   /* { dg-warning "writing up to 18 bytes" } */
+
+  T ( 0, "%.*ls", IR ( 0,  1),   WR (0, 9));   /* { dg-warning "writing up to 1 byte" } */
+  T ( 0, "%.*ls", IR ( 0,  2),   WR (0, 9));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*ls", IR ( 0,  9),   WR (0, 9));   /* { dg-warning "writing up to 9 bytes" } */
+  T ( 0, "%.*ls", IR ( 0, 53),   WR (0, 9));   /* { dg-warning "writing up to 53 bytes" } */
+  T ( 0, "%.*ls", IR ( 0, 55),   WR (0, 9));   /* { dg-warning "writing up to 54 bytes" } */
+  T ( 0, "%.*ls", IR ( 1,  2),   WR (0, 9));   /* { dg-warning "writing up to 2 bytes" } */
+  T ( 0, "%.*ls", IR ( 2,  3),   WR (0, 9));   /* { dg-warning "writing up to 3 bytes" } */
+  T ( 0, "%.*ls", IR ( 7, 13),   WR (0, 9));   /* { dg-warning "writing up to 13 bytes" } */
+
+  /* String between N and unknown number of characters long.  */
+  T ( 0, "%.*ls", IR (imin, -1), WR (0, x));   /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%.*ls", IR (imin, -1), WR (1, x));   /* { dg-warning "writing likely 2 or more bytes" } */
+  T ( 1, "%.*ls", IR (imin, -1), WR (1, x));   /* { dg-warning "writing likely 2 or more bytes" } */
+  T ( 1, "%.*ls", IR (imin, -1), WR (8, x));   /* { dg-warning "writing likely 16 or more bytes" } */
+  T ( 1, "%.*ls", IR (imin, -1), WR (x, 9));   /* { dg-warning "writing likely 18 or more bytes" } */
+
+  /* Unknown strings.  */
+  T ( 1, "%.*ls", IR (imin, -1), WR (x, y));
+  T ( 1, "%.*ls", IR (imin,  0), WR (x, y));
+  T ( 1, "%.*ls", IR ( -99,  1), WR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*ls", IR (  -2,  2), WR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*ls", IR (  -1, 99), WR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*ls", IR (   0, 99), WR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*ls", IR (   1, 99), WR (x, y));   /* { dg-warning "may write a terminating nul" } */
+  T ( 1, "%.*ls", IR (   9, 99), WR (x, y));   /* { dg-warning "may write a terminating nul" } */
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79275.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79275.c
new file mode 100644 (file)
index 0000000..d314524
--- /dev/null
@@ -0,0 +1,14 @@
+/* PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX
+   in glibc sysdeps/posix/tempname.c
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void f (char *dst, size_t n, const char *s)
+{
+  if (n < 2 || __INT_MAX__ - 2 < n)
+    n = 2;
+
+  __builtin_sprintf (dst, "%.*s %.*s", (int)n, s, (int)n, s);   /* { dg-bogus "INT_MAX" } */
+}