PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning
authorMartin Sebor <msebor@redhat.com>
Tue, 14 Feb 2017 16:51:24 +0000 (16:51 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Tue, 14 Feb 2017 16:51:24 +0000 (09:51 -0700)
gcc/testsuite/ChangeLog:

PR middle-end/79448
* gcc.dg/tree-ssa/builtin-snprintf-warn-3.c: New test.
* gcc.dg/tree-ssa/pr79448-2.c: New test.
* gcc.dg/tree-ssa/pr79448.c: New test.

gcc/ChangeLog:

PR middle-end/79448
* gimple-ssa-sprintf.c (format_directive): Avoid issuing INT_MAX
  warning for strings of unknown length.

From-SVN: r245437

gcc/ChangeLog
gcc/gimple-ssa-sprintf.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/pr79448-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/pr79448.c [new file with mode: 0644]

index bd6fdb48012648fd3e75ea2cb26f3ccf299381dd..3d0e95e29d2d29ec43f20c443674ec32a2303d83 100644 (file)
@@ -1,3 +1,9 @@
+2017-02-14  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/79448
+       * gimple-ssa-sprintf.c (format_directive): Avoid issuing INT_MAX
+         warning for strings of unknown length.
+
 2017-02-13  Segher Boessenkool  <segher@kernel.crashing.org>
 
        * config.gcc (supported_defaults) [powerpc*-*-*]: Update.
index b2db8b848b08bd61281933dc740d4ab5ffb80cc7..a5fc3ffd725da1d14a05441deeb325264ec8067a 100644 (file)
@@ -2559,13 +2559,16 @@ format_directive (const pass_sprintf_length::call_info &info,
     res->range.max += fmtres.range.max;
 
   /* Raise the total unlikely maximum by the larger of the maximum
-     and the unlikely maximum.  It doesn't matter if the unlikely
-     maximum overflows.  */
+     and the unlikely maximum.  */
+  unsigned HOST_WIDE_INT save = res->range.unlikely;
   if (fmtres.range.max < fmtres.range.unlikely)
     res->range.unlikely += fmtres.range.unlikely;
   else
     res->range.unlikely += fmtres.range.max;
 
+  if (res->range.unlikely < save)
+    res->range.unlikely = HOST_WIDE_INT_M1U;
+
   res->range.min += fmtres.range.min;
   res->range.likely += fmtres.range.likely;
 
@@ -2616,7 +2619,12 @@ format_directive (const pass_sprintf_length::call_info &info,
 
   /* Has the likely and maximum directive output exceeded INT_MAX?  */
   bool likelyximax = *dir.beg && res->range.likely > target_int_max ();
-  bool maxximax = *dir.beg && res->range.max > target_int_max ();
+  /* Don't consider the maximum to be in excess when it's the result
+     of a string of unknown length (i.e., whose maximum has been set
+     to be greater than or equal to HOST_WIDE_INT_MAX.  */
+  bool maxximax = (*dir.beg
+                  && res->range.max > target_int_max ()
+                  && res->range.max < HOST_WIDE_INT_MAX);
 
   if (!warned
       /* Warn for the likely output size at level 1.  */
index fb5b69d42984634f69140b2ab12cb49645871d8c..6e42393e9954d6de2e43baa2031af9e32b93e801 100644 (file)
@@ -1,3 +1,10 @@
+2017-02-14  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/79448
+       * gcc.dg/tree-ssa/builtin-snprintf-warn-3.c: New test.
+       * gcc.dg/tree-ssa/pr79448-2.c: New test.
+       * gcc.dg/tree-ssa/pr79448.c: New test.
+
 2017-02-14  Jeff Law  <law@redhat.com>
 
        PR tree-optimization/79095
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c
new file mode 100644 (file)
index 0000000..81c1d89
--- /dev/null
@@ -0,0 +1,193 @@
+/* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning
+   { dg-do compile }
+   { dg-options "-O2 -Wformat -Wformat-truncation=2 -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;
+}
+
+void sink (int, char*, char*);
+
+int dummy_snprintf (char*, size_t, const char*, ...);
+
+char fixed_buffer [256];
+extern char *unknown_buffer;
+extern size_t unknown_size;
+
+/* 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)
+
+/* Helper test macro.  */
+#define T(size, ...)                                   \
+  do {                                                 \
+    size_t n = size < 0 ? unknown_size : size;         \
+    char *buf = size < 0 ? unknown_buffer              \
+      : n < sizeof fixed_buffer                                \
+      ? fixed_buffer + sizeof fixed_buffer - size      \
+      : unknown_buffer;                                        \
+    FUNC (snprintf) (buf, n, __VA_ARGS__);             \
+    sink (0, fixed_buffer, unknown_buffer);            \
+  } while (0)
+
+/* Return a value in the range [MIN, MAX].  */
+#define IR(min, max)  int_range (min, max)
+
+struct Arrays
+{
+  char a1[1];
+  char a4k[4096];
+  char a4kp1[4097];
+#if INT_MAX < LONG_MAX
+  char amax[INT_MAX];
+#else
+  char amax[32767];
+#endif
+  char ax[];
+};
+
+void test_string_unchecked (const char *s, const struct Arrays *ar)
+{
+  /* Verify there is no warning with strings of unknown length.  */
+  T (-1, "%-s", s);
+  T (-1, "%-s", ar->ax);
+
+  T (-1, "%s%s", s, s);
+  T (-1, "%s%s", "", s);
+  T (-1, "%s%s", s, "1");
+  T (-1, "%s%s", "1", s);
+
+  /* Verify there is no warning with strings of length that cannot
+     exceed 4k (because of the array size).  */
+  T (-1, "%-s", ar->a1);
+  T (-1, "%-s", ar->a4k);
+
+  /* Verify there's no "exceeds minimum required size of 4095" warning
+     with multiple %s directives and a combination of strings of unknown
+     (and potentially unbounded) length and strings whose length is
+     bounded by the size of the arrays they are stored in.  */
+  T (-1, "%s%s", s, ar->a4k);
+  T (-1, "%s%s", ar->a4k, s);
+  T (-1, "%s%s", ar->a4k, ar->a4k);
+  T (-1, "%s%s", ar->a4k, "123");
+  T (-1, "%s%s", "123", ar->a4k);
+  T (-1, "%s%s", ar->ax, ar->a4k);
+  T (-1, "%s%s", ar->a4k, ar->ax);
+
+  /* Verify that an array that fits a string longer than 4095 bytes
+     does trigger a warning.  */
+  T (-1, "%-s", ar->a4kp1);   /* { dg-warning "directive output between 0 and 4096 bytes may exceed minimum required size of 4095" } */
+
+  /* Also verify that a %s directive with width greater than 4095
+     triggers a warning even if the argument is not longer than 4k.  */
+  T (-1, "%*s", 4096, ar->a4k);   /* { dg-warning "directive output of 4096 bytes exceeds minimum required size of 4095" } */
+
+  /* Verify that precision constrains the putput and suppresses the 4k
+     warning.  */
+  T (-1, "%.*s", 4095, ar->a4kp1);
+
+  T (-1, "%s %s", s, "");
+  T (-1, "%s %s", "", s);
+  T (-1, "%s %s", s, "1");
+  T (-1, "%s %s", "1", s);
+
+  T (-1, "%s%s%s", s, "1", s);
+  T (-1, "%s%s%s", "1", s, "1");
+  T (-1, "%s%s%s", s, s, s);
+  T (-1, "%*s%*s%*s", 4093, s, 4094, s, 4095, s);
+  T (-1, "%s %s %s", s, s, s);
+  T (-1, "%s %s %s", ar->a4k, ar->a4k, ar->a4k);
+  T (-1, "%s %s %s", ar->ax, ar->ax, ar->ax);
+
+  /* Verify that an array of INT_MAX elements doesn't trigger the INT_MAX
+     warning (LP64 only).  */
+  T (-1, "%-s", ar->amax);   /* { dg-warning "directive output between 0 and \[0-9\]+ bytes may exceed minimum required size of 4095" } */
+}
+
+#undef T
+/* Helper test macro.  */
+#define T(size, ...)                                   \
+  do {                                                 \
+    size_t n = size < 0 ? unknown_size : size;         \
+    char *buf = size < 0 ? unknown_buffer              \
+      : n < sizeof fixed_buffer                                \
+      ? fixed_buffer + sizeof fixed_buffer - size      \
+      : unknown_buffer;                                        \
+    int r = FUNC (snprintf) (buf, n, __VA_ARGS__);     \
+    sink (r, fixed_buffer, unknown_buffer);            \
+  } while (0)
+
+void test_string_checked (const char *s, const struct Arrays *ar)
+{
+  /* Verify there is no warning with strings of unknown length.  */
+  T (-1, "%-s", s);
+  T (-1, "%-s", ar->ax);
+
+  T (-1, "%s%s", s, s);
+  T (-1, "%s%s", "", s);
+  T (-1, "%s%s", s, "1");
+  T (-1, "%s%s", "1", s);
+
+  /* Verify there is no warning with strings of length that cannot
+     exceed 4k (because of the array size).  */
+  T (-1, "%-s", ar->a1);
+  T (-1, "%-s", ar->a4k);
+
+  /* Verify there's no "exceeds minimum required size of 4095" warning
+     with multiple %s directives and a combination of strings of unknown
+     (and potentially unbounded) length and strings whose length is
+     bounded by the size of the arrays they are stored in.  */
+  T (-1, "%s%s", s, ar->a4k);
+  T (-1, "%s%s", ar->a4k, s);
+  T (-1, "%s%s", ar->a4k, ar->a4k);
+  T (-1, "%s%s", ar->a4k, "123");
+  T (-1, "%s%s", "123", ar->a4k);
+  T (-1, "%s%s", ar->ax, ar->a4k);
+  T (-1, "%s%s", ar->a4k, ar->ax);
+
+  /* Verify that an array that fits a string longer than 4095 bytes
+     does trigger a warning.  */
+  T (-1, "%-s", ar->a4kp1);   /* { dg-warning "directive output between 0 and 4096 bytes may exceed minimum required size of 4095" } */
+
+  /* Also verify that a %s directive with width greater than 4095
+     triggers a warning even if the argument is not longer than 4k.  */
+  T (-1, "%*s", 4096, ar->a4k);   /* { dg-warning "directive output of 4096 bytes exceeds minimum required size of 4095" } */
+
+  /* Verify that precision constrains the putput and suppresses the 4k
+     warning.  */
+  T (-1, "%.*s", 4095, ar->a4kp1);
+
+  T (-1, "%s %s", s, "");
+  T (-1, "%s %s", "", s);
+  T (-1, "%s %s", s, "1");
+  T (-1, "%s %s", "1", s);
+
+  T (-1, "%s%s%s", s, "1", s);
+  T (-1, "%s%s%s", "1", s, "1");
+  T (-1, "%s%s%s", s, s, s);
+  T (-1, "%*s%*s%*s", 4093, s, 4094, s, 4095, s);
+  T (-1, "%s %s %s", s, s, s);
+  T (-1, "%s %s %s", ar->a4k, ar->a4k, ar->a4k);
+  T (-1, "%s %s %s", ar->ax, ar->ax, ar->ax);
+
+  T (-1, "%-s", ar->amax);   /* { dg-warning "directive output between 0 and \[0-9\]+ bytes may exceed minimum required size of 4095" } */
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79448-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79448-2.c
new file mode 100644 (file)
index 0000000..f75f523
--- /dev/null
@@ -0,0 +1,21 @@
+/* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning
+   Verify that there's no warning with optimization.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wformat -Wformat-truncation=2" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern int
+snprintf (char*, size_t, const char*, ...);
+
+char*
+gettext (char*);
+
+char*
+fill (char *buf, size_t len, int count)
+{
+  if (snprintf (buf, len, "%s: %d", gettext ("count"), count) >= len)  /* { dg-bogus "directive output of 2 bytes causes result to exceed .INT_MAX." */
+    return 0;
+
+  return buf;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79448.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79448.c
new file mode 100644 (file)
index 0000000..c346c9e
--- /dev/null
@@ -0,0 +1,21 @@
+/* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning
+   Verify that there's no warning without optimization.
+   { dg-do compile }
+   { dg-options "-Wall -Wformat -Wformat-truncation=2" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern int
+snprintf (char*, size_t, const char*, ...);
+
+char*
+gettext (char*);
+
+char*
+fill (char *buf, size_t len, int count)
+{
+  if (snprintf (buf, len, "%s: %d", gettext ("count"), count) >= len)  /* { dg-bogus "directive output of 2 bytes causes result to exceed .INT_MAX." */
+    return 0;
+
+  return buf;
+}