PR tree-optimization/ 79376 - wrong lower bound with %s and non-constant
authorMartin Sebor <msebor@redhat.com>
Mon, 6 Feb 2017 20:11:51 +0000 (20:11 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Mon, 6 Feb 2017 20:11:51 +0000 (13:11 -0700)
strings in -Wformat-overflow

gcc/ChangeLog:
* gimple-fold.c (get_range_strlen): Set the minimum length to zero.

From-SVN: r245221

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

index a29c20e35cae3ebbfa606ddde347be1015ee9a9e..76dc97edd3507fbd6a3fbfc86804cbf0615f00ec 100644 (file)
@@ -1,3 +1,8 @@
+2017-02-06  Martin Sebor  <msebor@redhat.com>
+
+       PR  tree-optimization/79376
+       * gimple-fold.c (get_range_strlen): Set the minimum length to zero.
+
 2017-02-06  Uros Bizjak  <ubizjak@gmail.com>
 
        * config/i386/sse.md (vector modes -> vec_extract* splitter): Use
index 1cd22a8bfdd3cea1e72ca6aa57eb82c6dd8bac81..a75dd917fe4e22a73047f820052dda1925c194a9 100644 (file)
@@ -1244,8 +1244,9 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
                return false;
              val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
                                 integer_one_node);
-             /* Avoid using the array size as the minimum.  */
-             minlen = NULL;
+             /* Set the minimum size to zero since the string in
+                the array could have zero length.  */
+             *minlen = ssize_int (0);
            }
        }
 
index 6737cf46e3cdb2ce9f0e8e214a0693643746bf9e..b29d283541942fd3abbe33f90621ab0fdb4881ea 100644 (file)
@@ -1,3 +1,9 @@
+2017-02-06  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/79376
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-14.c: New test.
+       * gcc.dg/tree-ssa/pr79376.c: Ditto.
+
 2017-02-06  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/79379
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-14.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-14.c
new file mode 100644 (file)
index 0000000..dc3f132
--- /dev/null
@@ -0,0 +1,210 @@
+/* PR middle-end/79376 - wrong lower bound with %s and non-constant
+   strings in -Wformat-overflow
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__  size_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);
+
+int int_range (int min, int max)
+{
+  int n = int_value ();
+  return n < min || max < n ? min : n;
+}
+
+const char*
+choose_string (const char *s1, const char *s2, const char *s3)
+{
+  int i = int_value ();
+  return i < 0 ? s1 : 0 < i ? s3 : s2;
+}
+
+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[];
+
+/* 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 R(min, max)  int_range (min, max)
+
+/* Return one of the strings S1, S2, S3.  */
+#define S(s1, s2, s3) choose_string (s1, s2, s3)
+
+struct S {
+  char a1[1];
+  char a2[2];
+  char a3[3];
+  char a4[4];
+  char a5[5];
+  char ax[];
+};
+
+void test_strings (struct S *s)
+{
+  T (0, "%-s", S (s->a1, s->a1, s1));   /* { dg-warning "writing up to 1 byte" } */
+  T (0, "%-s", S (s->a1, s->a2, s1));   /* { dg-warning "writing up to 1 byte" } */
+  T (0, "%-s", S (s->a2, s->a1, s1));   /* { dg-warning "writing up to 1 byte" } */
+
+  T (0, "%-s", S (s->a1, s1, s->a1));   /* { dg-warning "writing up to 1 byte" } */
+  T (0, "%-s", S (s->a1, s1, s->a2));   /* { dg-warning "writing up to 1 byte" } */
+  T (0, "%-s", S (s->a2, s1, s->a1));   /* { dg-warning "writing up to 1 byte" } */
+
+  T (0, "%-s", S (s1, s->a1, s1));      /* { dg-warning "writing up to 1 byte" } */
+  T (0, "%-s", S (s1, s->a1, s1));      /* { dg-warning "writing up to 1 byte" } */
+  T (0, "%-s", S (s1, s->a2, s1));      /* { dg-warning "writing up to 1 byte" } */
+
+  T (0, "%-s", S (s->a1, s->a1, s2));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a1, s->a2, s2));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a2, s->a1, s2));   /* { dg-warning "writing up to 2 bytes" } */
+
+  T (0, "%-s", S (s->a1, s2, s->a1));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a1, s2, s->a2));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a2, s2, s->a1));   /* { dg-warning "writing up to 2 bytes" } */
+
+  T (0, "%-s", S (s2, s->a1, s1));      /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s2, s->a1, s1));      /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s2, s->a2, s1));      /* { dg-warning "writing up to 2 bytes" } */
+
+  T (0, "%-s", S (s->a1, s->a1, s2));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a1, s->a2, s2));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a2, s->a1, s2));   /* { dg-warning "writing up to 2 bytes" } */
+
+  T (0, "%-s", S (s->a1, s2, s->a1));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a1, s2, s->a2));   /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s->a2, s2, s->a1));   /* { dg-warning "writing up to 2 bytes" } */
+
+  T (0, "%-s", S (s2, s->a1, s1));      /* { dg-warning "writing up to 2 bytes" } */
+  T (0, "%-s", S (s3, s->a1, s2));      /* { dg-warning "writing up to 3 bytes" } */
+  T (0, "%-s", S (s4, s->a2, s3));      /* { dg-warning "writing up to 4 bytes" } */
+
+  T (0, "%-s", S (s->a3, s->a5, s3));   /* { dg-warning "writing up to 4 bytes" } */
+  T (0, "%-s", S (s->a3, s->a5, s3));   /* { dg-warning "writing up to 4 bytes" } */
+  T (0, "%-s", S (s->a3, s->a5, s3));   /* { dg-warning "writing up to 4 bytes" } */
+
+  T (0, "%-s", S (s->a3, s3, s->a5));   /* { dg-warning "writing up to 4 bytes" } */
+  T (0, "%-s", S (s->a5, s3, s->a3));   /* { dg-warning "writing up to 4 bytes" } */
+  T (0, "%-s", S (s->a3, s3, s->a5));   /* { dg-warning "writing up to 4 bytes" } */
+
+  T (0, "%-s", S (s3, s->a1, s4));      /* { dg-warning "writing up to 4 bytes" } */
+  T (0, "%-s", S (s4, s->a2, s3));      /* { dg-warning "writing up to 4 bytes" } */
+}
+
+void test_strings_with_width (struct S *s)
+{
+  T (0, "%*s",                          /* { dg-warning "writing up to 4 bytes" } */
+     R (0, 1),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*s",                          /* { dg-warning "writing up to 4 bytes" } */
+     R (0, 4),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*s",                          /* { dg-warning "writing between 1 and 4 bytes" } */
+     R (1, 4),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*s",                          /* { dg-warning "writing between 2 and 4 bytes" } */
+     R (2, 4),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*s",                          /* { dg-warning "writing between 3 and 4 bytes" } */
+     R (3, 4),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*s",                          /* { dg-warning "writing between 3 and 5 bytes" } */
+     R (3, 5),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*s",                          /* { dg-warning "writing between 3 and 9 bytes" } */
+     R (3, 9),
+     S (s->a3, s->a5, s3));
+
+  T (3, "%*s",                          /* { dg-warning "writing between 3 and 9 bytes" } */
+     R (3, 9),
+     S (s->a3, s->a5, s3));
+
+  /* The longest string fits but the terminating nul will overflow.  Since
+     the null won't overflow with the shorter strings the warning is a "may
+     write."  */
+  T (4, "%*s",                          /* { dg-warning "may write a terminating nul" } */
+     R (3, 9),
+     S (s->a3, s->a5, s3));
+}
+
+void test_strings_with_width_and_precisin (struct S *s)
+{
+  T (0, "%*.*s",                        /* { dg-warning "writing up to 1 byte" } */
+     R (0, 1),
+     R (0, 1),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*.*s",                        /* { dg-warning "writing up to 2 bytes" } */
+     R (0, 2),
+     R (0, 1),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*.*s",                        /* { dg-warning "writing up to 9 bytes" } */
+     R (0, 9),
+     R (0, 1),
+     S (s->a3, s->a5, s3));
+
+  /* Since the longest string/array fits there is no warning even if
+     the maximum width would cause overflow.  */
+  T (6, "%*.*s",
+     R (0, 9),
+     R (0, 1),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*.*s",                        /* { dg-warning "writing up to 2 bytes" } */
+     R (0, 2),
+     R (0, 2),
+     S (s->a3, s->a5, s3));
+
+  T (0, "%*.*s",                        /* { dg-warning "writing up to 3 bytes" } */
+     R (0, 3),
+     R (0, 2),
+     S (s->a3, s->a5, s3));
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c
new file mode 100644 (file)
index 0000000..838bfe0
--- /dev/null
@@ -0,0 +1,109 @@
+/* PR tree-optimization/79376 - wrong lower bound with %s and non-constant
+   strings in -Wformat-overflow
+   { dg-compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define CAT(s, n)   s ## n
+#define FAIL(line)  CAT (failure_on_line_, line)
+
+/* Emit a call to a function named failure_on_line_NNN when EXPR is false.  */
+#define ASSERT(expr)                           \
+  do {                                         \
+    extern void FAIL (__LINE__)(void);         \
+    if (!(expr)) FAIL (__LINE__)();            \
+  } while (0)
+
+#define KEEP(line)  CAT (keep_call_on_line_, line)
+
+/* Emit a call to a function named keep_call_on_line_NNN when EXPR is true.
+   Used to verify that the expression need not be the only one that holds.  */
+#define ASSERT_MAYBE(expr)                     \
+  do {                                         \
+    extern void KEEP (__LINE__)(void);         \
+    if (expr) KEEP (__LINE__)();               \
+  } while (0)
+
+struct Arrays
+{
+  char a1[1];
+  char a2[2];
+  char a3[3];
+  char a4[4];
+  char a5[5];
+  char ax[];
+};
+
+void test_arrays (int i, struct Arrays *a)
+{
+  {
+    const char *s = i < 0 ? a->a3 : a->a1;
+
+    int n = __builtin_snprintf (0, 0, "%-s", s);
+
+    ASSERT (0 <= n && n < 3);
+
+    ASSERT_MAYBE (0 == n);
+    ASSERT_MAYBE (1 == n);
+    ASSERT_MAYBE (2 == n);
+  }
+
+  {
+    const char *s = i < 0 ? a->a3 : a->a5;
+
+    int n = __builtin_snprintf (0, 0, "%-s", s);
+
+    ASSERT (0 <= n && n < 5);
+
+    ASSERT_MAYBE (0 == n);
+    ASSERT_MAYBE (1 == n);
+    ASSERT_MAYBE (2 == n);
+    ASSERT_MAYBE (3 == n);
+    ASSERT_MAYBE (4 == n);
+  }
+}
+
+void test_string_and_array (int i, struct Arrays *a)
+{
+  {
+    const char *s = i < 0 ? a->a3 : "1";
+
+    int n = __builtin_snprintf (0, 0, "%-s", s);
+
+    ASSERT (0 <= n && n < 3);
+
+    ASSERT_MAYBE (0 == n);
+    ASSERT_MAYBE (1 == n);
+    ASSERT_MAYBE (2 == n);
+  }
+
+  {
+    const char *s = i < 0 ? "12" : a->a5;
+
+    int n = __builtin_snprintf (0, 0, "%-s", s);
+
+    ASSERT (0 <= n && n < 5);
+
+    ASSERT_MAYBE (0 == n);
+    ASSERT_MAYBE (1 == n);
+    ASSERT_MAYBE (2 == n);
+    ASSERT_MAYBE (3 == n);
+    ASSERT_MAYBE (4 == n);
+  }
+
+  {
+    const char *s = i < 0 ? a->a4 : 0 < i ? "12" : a->a5;
+
+    int n = __builtin_snprintf (0, 0, "%-s", s);
+
+    ASSERT (0 <= n && n < 5);
+
+    ASSERT_MAYBE (0 == n);
+    ASSERT_MAYBE (1 == n);
+    ASSERT_MAYBE (2 == n);
+    ASSERT_MAYBE (3 == n);
+    ASSERT_MAYBE (4 == n);
+  }
+}
+
+/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized"} }
+   { dg-final { scan-tree-dump-times "keep_call_on_line" 21 "optimized"} } */