From: Martin Sebor Date: Mon, 18 Dec 2017 22:49:57 +0000 (+0000) Subject: PR middle-end/83373 - False positive reported by -Wstringop-overflow X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=06199618c1c047366fdafd2b0fafdace1fb44abc;p=gcc.git PR middle-end/83373 - False positive reported by -Wstringop-overflow 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 948271e28e6..d165b5ccf41 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2017-12-18 Martin Sebor + + 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 PR rtl-optimization/83424 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 129d142a83c..eb2eba70d66 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2017-12-18 Martin Sebor + + 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 PR c++/83116 diff --git a/gcc/testsuite/gcc.dg/pr83373.c b/gcc/testsuite/gcc.dg/pr83373.c new file mode 100644 index 00000000000..6b0de0997c0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr83373.c @@ -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 index 00000000000..d6fcca26b97 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-36.c @@ -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 index 00000000000..865653c18d3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-37.c @@ -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" } } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index e75d13392f6..0386883a749 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -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);