PR middle-end/83373 - False positive reported by -Wstringop-overflow
authorMartin Sebor <msebor@redhat.com>
Mon, 18 Dec 2017 22:49:57 +0000 (22:49 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Mon, 18 Dec 2017 22:49:57 +0000 (15:49 -0700)
PR middle-end/83373 - False positive reported by -Wstringop-overflow
PR tree-optimization/78450 - strlen(s) return value can be assumed to be less than the size of s

gcc/ChangeLog:

PR middle-end/83373
PR tree-optimization/78450
* tree-ssa-strlen.c (maybe_set_strlen_range): New function.
(handle_builtin_strlen): Call it.

gcc/testsuite/ChangeLog:

PR middle-end/83373
PR tree-optimization/78450
* gcc.dg/pr83373.c: New test.
* gcc.dg/strlenopt-36.c: New test.
* gcc.dg/strlenopt-37.c: New test.

From-SVN: r255790

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/pr83373.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strlenopt-36.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strlenopt-37.c [new file with mode: 0644]
gcc/tree-ssa-strlen.c

index 948271e28e6b50078d7c705d41324d48c4d86b76..d165b5ccf410f4bca19bfb896df3bfecaf1e3865 100644 (file)
@@ -1,3 +1,10 @@
+2017-12-18  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/83373
+       PR tree-optimization/78450
+       * tree-ssa-strlen.c (maybe_set_strlen_range): New function.
+       (handle_builtin_strlen): Call it.
+
 2017-12-18  Segher Boessenkool  <segher@kernel.crashing.org>
 
        PR rtl-optimization/83424
index 129d142a83cb1d2f8a7ccbba40162d765064d6c7..eb2eba70d6698bf3ea88e0253f8f5167f11de28e 100644 (file)
@@ -1,3 +1,11 @@
+2017-12-18  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/83373
+       PR tree-optimization/78450
+       * gcc.dg/pr83373.c: New test.
+       * gcc.dg/strlenopt-36.c: New test.
+       * gcc.dg/strlenopt-37.c: New test.
+
 2017-12-18  Marek Polacek  <polacek@redhat.com>
 
        PR c++/83116
diff --git a/gcc/testsuite/gcc.dg/pr83373.c b/gcc/testsuite/gcc.dg/pr83373.c
new file mode 100644 (file)
index 0000000..6b0de09
--- /dev/null
@@ -0,0 +1,33 @@
+/* PR middle-end/83373 - False positive reported by -Wstringop-overflow
+   { dg-do compile }
+   { dg-options "-O2 -Wstringop-overflow" }  */
+
+typedef __SIZE_TYPE__ size_t;
+
+char buf[100];
+
+void get_data (char*);
+
+__attribute__ ((nonnull(1, 2)))
+inline char* my_strcpy (char* dst, const char* src, size_t size)
+{
+  size_t len = __builtin_strlen (src);
+  if (len < size)
+    __builtin_memcpy (dst, src, len + 1);
+  else
+    {
+      __builtin_memcpy (dst, src, size - 1); /* { dg-bogus "\\\[-Wstringop-oveflow]" } */
+      dst[size - 1] = '\0';
+    }
+
+  return dst;
+}
+
+void test(void)
+{
+  char data[20] = "12345";
+
+  get_data (data);
+
+  my_strcpy (buf, data, sizeof buf);
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-36.c b/gcc/testsuite/gcc.dg/strlenopt-36.c
new file mode 100644 (file)
index 0000000..d6fcca2
--- /dev/null
@@ -0,0 +1,86 @@
+/* PR tree-optimization/78450 - strlen(s) return value can be assumed
+   to be less than the size of s
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+extern char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
+extern char a0[0];   /* Intentionally not tested here.  */
+extern char ax[];    /* Same.  */
+
+struct MemArrays {
+  char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
+  char a0[0];   /* Not tested here.  */
+};
+
+struct NestedMemArrays {
+  struct { char a7[7]; } ma7;
+  struct { char a6[6]; } ma6;
+  struct { char a5[5]; } ma5;
+  struct { char a4[4]; } ma4;
+  struct { char a3[3]; } ma3;
+  struct { char a2[2]; } ma2;
+  struct { char a1[1]; } ma1;
+  struct { char a0[0]; } ma0;
+  char last;
+};
+
+extern void failure_on_line (int);
+
+#define TEST_FAIL(line)                                        \
+  do {                                                 \
+    failure_on_line (line);                            \
+  } while (0)
+
+#define T(expr)                                                \
+  if (!(expr)) TEST_FAIL (__LINE__); else (void)0
+
+
+void test_array (void)
+{
+  T (strlen (a7) < sizeof a7);
+  T (strlen (a6) < sizeof a6);
+  T (strlen (a5) < sizeof a5);
+  T (strlen (a4) < sizeof a4);
+  T (strlen (a3) < sizeof a3);
+
+  /* The following two calls are folded too early which defeats
+     the strlen() optimization.
+    T (strlen (a2) == 1);
+    T (strlen (a1) == 0);  */
+}
+
+void test_memarray (struct MemArrays *ma)
+{
+  T (strlen (ma->a7) < sizeof ma->a7);
+  T (strlen (ma->a6) < sizeof ma->a6);
+  T (strlen (ma->a5) < sizeof ma->a5);
+  T (strlen (ma->a4) < sizeof ma->a4);
+  T (strlen (ma->a3) < sizeof ma->a3);
+
+  /* The following two calls are folded too early which defeats
+     the strlen() optimization.
+  T (strlen (ma->a2) == 1);
+  T (strlen (ma->a1) == 0);  */
+}
+
+/* Verify that the range of strlen(A) of a last struct member is
+   set even when the array is the sole member of a struct as long
+   as the struct itself is a member of another struct.  The converse
+   is tested in stlenopt-37.c.  */
+void test_nested_memarray (struct NestedMemArrays *ma)
+{
+  T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
+  T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
+  T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
+  T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
+  T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
+
+  /* The following two calls are folded too early which defeats
+     the strlen() optimization.
+  T (strlen (ma->ma2.a2) == 1);
+  T (strlen (ma->ma1.a1) == 0);  */
+}
+
+/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-37.c b/gcc/testsuite/gcc.dg/strlenopt-37.c
new file mode 100644 (file)
index 0000000..865653c
--- /dev/null
@@ -0,0 +1,83 @@
+/* PR tree-optimization/78450 - strlen(s) return value can be assumed
+   to be less than the size of s
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+extern char ax[];
+
+struct MemArray7 { char a7[7]; };
+struct MemArray6 { char a6[6]; };
+struct MemArray5 { char a5[5]; };
+struct MemArray4 { char a4[4]; };
+struct MemArray3 { char a3[3]; };
+struct MemArray2 { char a2[2]; };
+struct MemArray1 { char a1[1]; };
+struct MemArray0 { int n; char a0[0]; };
+struct MemArrayX { int n; char ax[]; };
+
+struct MemArrays
+{
+  struct MemArray7 *ma7;
+  struct MemArray6 *ma6;
+  struct MemArray5 *ma5;
+  struct MemArray4 *ma4;
+  struct MemArray3 *ma3;
+  struct MemArray2 *ma2;
+  struct MemArray1 *ma1;
+  struct MemArray0 *ma0;
+  struct MemArrayX *max;
+};
+
+extern void if_stmt_on_line (int);
+extern void else_stmt_on_line (int);
+
+#define T(expr)                                                                \
+  (!!(expr) ? if_stmt_on_line (__LINE__) : else_stmt_on_line (__LINE__))
+
+void test_memarray_lt (struct MemArrays *p)
+{
+  T (strlen (p->ma7->a7) < sizeof p->ma7->a7);
+  T (strlen (p->ma6->a6) < sizeof p->ma6->a6);
+  T (strlen (p->ma5->a5) < sizeof p->ma5->a5);
+  T (strlen (p->ma4->a4) < sizeof p->ma4->a4);
+  T (strlen (p->ma3->a3) < sizeof p->ma3->a3);
+  T (strlen (p->ma2->a2) < sizeof p->ma2->a2);
+  T (strlen (p->ma1->a1) < sizeof p->ma1->a1);
+
+  T (strlen (p->ma0->a0) < 1);
+  T (strlen (p->max->ax) < 1);
+}
+
+void test_memarray_eq (struct MemArrays *p)
+{
+  T (strlen (p->ma7->a7) == sizeof p->ma7->a7);
+  T (strlen (p->ma6->a6) == sizeof p->ma6->a6);
+  T (strlen (p->ma5->a5) == sizeof p->ma5->a5);
+  T (strlen (p->ma4->a4) == sizeof p->ma4->a4);
+  T (strlen (p->ma3->a3) == sizeof p->ma3->a3);
+  T (strlen (p->ma2->a2) == sizeof p->ma2->a2);
+  T (strlen (p->ma1->a1) == sizeof p->ma1->a1);
+
+  T (strlen (p->ma0->a0) == 1);
+  T (strlen (p->max->ax) == 1);
+}
+
+void test_memarray_gt (struct MemArrays *p)
+{
+  T (strlen (p->ma7->a7) > sizeof p->ma7->a7);
+  T (strlen (p->ma6->a6) > sizeof p->ma6->a6);
+  T (strlen (p->ma5->a5) > sizeof p->ma5->a5);
+  T (strlen (p->ma4->a4) > sizeof p->ma4->a4);
+  T (strlen (p->ma3->a3) > sizeof p->ma3->a3);
+  T (strlen (p->ma2->a2) > sizeof p->ma2->a2);
+  T (strlen (p->ma1->a1) > sizeof p->ma1->a1);
+
+  T (strlen (p->ma0->a0) > 1);
+  T (strlen (p->max->ax) > 1);
+ }
+
+/* Verify that no if or else statements have been eliminated.
+   { dg-final { scan-tree-dump-times "if_stmt_on_line" 27 "optimized" } }
+   { dg-final { scan-tree-dump-times "else_stmt_on_line" 27 "optimized" } }  */
index e75d13392f6e3ee48ebf4abca20faeed375cd162..0386883a74902eedaca6466b9a08113a1179472f 100644 (file)
@@ -1152,6 +1152,44 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
   update_stmt (last.stmt);
 }
 
+/* For an LHS that is an SSA_NAME and for strlen() argument SRC, set
+   LHS range info to [0, N] if SRC refers to a character array A[N]
+   with unknown length bounded by N.  */
+
+static void
+maybe_set_strlen_range (tree lhs, tree src)
+{
+  if (TREE_CODE (lhs) != SSA_NAME)
+    return;
+
+  if (TREE_CODE (src) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (src);
+      if (is_gimple_assign (def)
+         && gimple_assign_rhs_code (def) == ADDR_EXPR)
+       src = gimple_assign_rhs1 (def);
+    }
+
+  if (TREE_CODE (src) != ADDR_EXPR)
+    return;
+
+  /* The last array member of a struct can be bigger than its size
+     suggests if it's treated as a poor-man's flexible array member.  */
+  src = TREE_OPERAND (src, 0);
+  if (TREE_CODE (TREE_TYPE (src)) != ARRAY_TYPE
+      || array_at_struct_end_p (src))
+    return;
+
+  tree type = TREE_TYPE (src);
+  if (tree dom = TYPE_DOMAIN (type))
+    if (tree maxval = TYPE_MAX_VALUE (dom))
+      {
+       wide_int max = wi::to_wide (maxval);
+       wide_int min = wi::zero (max.get_precision ());
+       set_range_info (lhs, VR_RANGE, min, max);
+      }
+}
+
 /* Handle a strlen call.  If strlen of the argument is known, replace
    the strlen call with the known value, otherwise remember that strlen
    of the argument is stored in the lhs SSA_NAME.  */
@@ -1262,6 +1300,10 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
       set_strinfo (idx, si);
       find_equal_ptrs (src, idx);
 
+      /* For SRC that is an array of N elements, set LHS's range
+        to [0, N].  */
+      maybe_set_strlen_range (lhs, src);
+
       if (strlen_to_stridx)
        {
          location_t loc = gimple_location (stmt);