From a76acaedcee2e75b16adfa0112632873c1fe2e71 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Wed, 7 Mar 2018 19:30:31 +0000 Subject: [PATCH] PR tree-optimization/84468 - bogus -Wstringop-truncation despite assignment after conditional strncpy gcc/ChangeLog: PR tree-optimization/84468 * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Consider successor basic block when looking for nul assignment. gcc/testsuite/ChangeLog: PR tree-optimization/84468 * g++.dg/warn/Wstringop-truncation-2.C: New test. * gcc.dg/Wstringop-truncation.c: New test. * gcc.dg/Wstringop-truncation-2.c: New test. From-SVN: r258339 --- gcc/ChangeLog | 6 + gcc/testsuite/ChangeLog | 7 + .../g++.dg/warn/Wstringop-truncation-1.C | 4 +- .../g++.dg/warn/Wstringop-truncation-2.C | 164 ++++++++++++++++++ gcc/testsuite/gcc.dg/Wstringop-truncation-2.c | 126 ++++++++++++++ gcc/testsuite/gcc.dg/Wstringop-truncation.c | 131 ++++++++++++++ gcc/tree-ssa-strlen.c | 27 ++- 7 files changed, 462 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C create mode 100644 gcc/testsuite/gcc.dg/Wstringop-truncation-2.c create mode 100644 gcc/testsuite/gcc.dg/Wstringop-truncation.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 00b79a553c9..1e988ed7199 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2018-03-07 Martin Sebor + + PR tree-optimization/84468 + * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Consider successor + basic block when looking for nul assignment. + 2018-03-07 Eric Botcazou PR target/84277 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2bf1abbd5b6..8e273c6bf6c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2018-03-07 Martin Sebor + + PR tree-optimization/84468 + * g++.dg/warn/Wstringop-truncation-2.C: New test. + * gcc.dg/Wstringop-truncation.c: New test. + * gcc.dg/Wstringop-truncation-2.c: New test. + 2018-03-07 Jakub Jelinek PR fortran/84565 diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-truncation-1.C b/gcc/testsuite/g++.dg/warn/Wstringop-truncation-1.C index a502b78b711..83066019772 100644 --- a/gcc/testsuite/g++.dg/warn/Wstringop-truncation-1.C +++ b/gcc/testsuite/g++.dg/warn/Wstringop-truncation-1.C @@ -37,7 +37,7 @@ void good_nowarn_size_m1 () sink (&str); } -void good_nowarn_size_m1_var (const char* s) +static void good_nowarn_size_m1_var (const char* s) { GoodString<3> str (s); // { dg-bogus "\\\[-Wstringop-truncation]" } sink (&str); @@ -112,7 +112,7 @@ private: char str[N + 1]; }; -void bad3_warn_size_m1_var (const char *s) +static void bad3_warn_size_m1_var (const char *s) { BadString3<3> str (s); sink (&str); diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C b/gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C new file mode 100644 index 00000000000..ebbd44307d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C @@ -0,0 +1,164 @@ +// PR tree-optimization/84468 - bogus -Wstringop-truncation despite +// assignment after conditional strncpy +// Compile with -g to verify the warning deals properly with debug +// statements. +// { dg-do compile } +// { dg-options "-O2 -Wstringop-truncation -g" } + +extern "C" char* strncpy (char*, const char*, __SIZE_TYPE__); + +char d[3]; + +void g (); + +void fnowarn1 (const char *s) +{ + // Update dummy but never actually use it so it's eliminated + // but causes debugging statements to be emitted for each + // modification. + int dummy = 0; + + try + { + g (); + strncpy (d, s, sizeof d); // { dg-bogus "\\\[-Wstringop-truncation]" } + ++dummy; + } + catch (...) + { + ++dummy; + d[0] = 0; + } + + ++dummy; + d[sizeof d - 1] = 0; +} + +void fnowarn2 (const char *s) +{ + int dummy = 0; + + try + { + g (); + strncpy (d, s, sizeof d); + ++dummy; + } + catch (...) + { + ++dummy; + return; + } + + ++dummy; + d[sizeof d - 1] = 0; +} + +void fnowarn3 (const char *s) +{ + int dummy = 0; + + try + { + g (); + strncpy (d, s, sizeof d); + ++dummy; + try + { + ++dummy; + d[sizeof d - 1] = 0; + g (); + } + catch (...) + { + ++dummy; + } + } + catch (...) + { + ++dummy; + return; + } + + ++dummy; + d[sizeof d - 1] = 0; +} + +void fnowarn4 (const char *s) +{ + int dummy = 0; + + try + { + g (); + } + catch (...) + { + strncpy (d, s, sizeof d); // { dg-bogus "\\\[-Wstringop-truncation]" "bug 84468" { xfail *-*-*} } + ++dummy; + } + + ++dummy; + d[sizeof d - 1] = 0; +} + +void fwarn1 (const char *s) +{ + int dummy = 0; + + try + { + ++dummy; + g (); + ++dummy; + strncpy (d, s, sizeof d); // { dg-warning "\\\[-Wstringop-truncation]" } + ++dummy; + } + catch (...) + { + ++dummy; + } + + ++dummy; +} + +void fwarn2 (const char *s) +{ + int dummy = 0; + + try + { + ++dummy; + strncpy (d, s, sizeof d); // { dg-warning "\\\[-Wstringop-truncation]" } + ++dummy; + g (); + ++dummy; + } + catch (...) + { + ++dummy; + } + + ++dummy; +} + +void fwarn3 (const char *s) +{ + int dummy = 0; + + try + { + ++dummy; + g (); + ++dummy; + strncpy (d, s, sizeof d); // { dg-warning "\\\[-Wstringop-truncation]" } + ++dummy; + } + catch (...) + { + ++dummy; + d[0] = 0; + } + + ++dummy; +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-truncation-2.c b/gcc/testsuite/gcc.dg/Wstringop-truncation-2.c new file mode 100644 index 00000000000..707a511d92e --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-truncation-2.c @@ -0,0 +1,126 @@ +/* PR tree-optimization/84468 - bogus -Wstringop-truncation despite + assignment after conditional strncpy + { dg-do compile } + { dg-options "-O2 -Wstringop-truncation -g" } */ + +extern char* strncpy (char*, const char*, __SIZE_TYPE__); + +char a[4]; + +void f1 (char *s) +{ + int i = 0; + + if (s[0] == '0') + { + i += 1; + strncpy (a, s, sizeof a); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + } + else + i += 2; + + a[sizeof a - 1] = 0; +} + +void f2 (char *s) +{ + int i = 0; + + if (s[0] == '0') + { + i += 1; + if (s[1] == '1') + { + i += 2; + strncpy (a, s, sizeof a); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + } + else + i += 3; + } + else + i += 4; + + a[sizeof a - 1] = 0; +} + +void f3 (char *s) +{ + int i = 0; + + if (s[0] == '0') + { + i += 1; + if (s[1] == '1') + { + i += 2; + if (s[2] == '2') + strncpy (a, s, sizeof a); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + else + i += 3; + } + else + i += 4; + } + else + i += 5; + + a[sizeof a - 1] = 0; +} + +void f4 (char *s) +{ + int i = 0; + + if (s[0] == '0') + { + i += 1; + if (s[1] == '1') + { + i += 2; + if (s[2] == '2') + { + i += 3; + if (s[3] == '3') + strncpy (a, s, sizeof a); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + else + i += 4; + } + else + i += 5; + } + else + i += 6; + } + else + i += 7; + + a[sizeof a - 1] = 0; +} + +void f4_warn (char *s) +{ + int i = 0; + + if (s[0] == '0') + { + i += 1; + if (s[1] == '1') + { + i += 2; + if (s[2] == '2') + { + i += 3; + if (s[3] == '3') + strncpy (a, s, sizeof a); /* { dg-warning "\\\[-Wstringop-truncation]" } */ + else + i += 4; + } + else + i += 5; + } + else + i += 6; + } + else + i += 7; +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-truncation.c b/gcc/testsuite/gcc.dg/Wstringop-truncation.c new file mode 100644 index 00000000000..b5372967790 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-truncation.c @@ -0,0 +1,131 @@ +/* PR tree-optimization/84468 - Inconsistent -Wstringop-truncation warnings + with -O2 + { dg-do compile } + { dg-options "-O2 -Wstringop-truncation -ftrack-macro-expansion=0 -g" } */ + +#define strncpy __builtin_strncpy + +struct A +{ + char a[4]; +}; + +void no_pred_succ_lit (struct A *p) +{ + /* The following is folded early on, before the strncpy statement + has a basic block. Verify that the case is handled gracefully + (i.e., there's no assumption that the statement does have + a basic block). */ + strncpy (p->a, "1234", sizeof p->a - 1); /* { dg-warning "\\\[-Wstringop-truncation" } */ +} + +/* Verify a strncpy call in a basic block with no predecessor or + successor. */ +void no_pred_succ (struct A *p, const struct A *q) +{ + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-warning "\\\[-Wstringop-truncation" } */ +} + + +/* Verify a strncpy call in a basic block with no successor. */ +void no_succ (struct A *p, const struct A *q) +{ + if (q->a) + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-warning "\\\[-Wstringop-truncation" } */ +} + +/* Verify a strncpy call in a basic block with nul assignment in + a successor block. */ +void succ (struct A *p, const struct A *q) +{ + /* Verify that the assignment suppresses the warning for the conditional + strcnpy call. The conditional should be folded to true since the + address of an array can never be null (see bug 84470). */ + if (q->a) + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + + p->a[sizeof p->a - 1] = 0; +} + + +void succ_2 (struct A *p, const struct A *q, int i) +{ + /* Same as above but with a conditional that cannot be eliminated. */ + if (i < 0) + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + + p->a[sizeof p->a - 1] = 0; +} + + +/* Verify a strncpy call in a basic block with nul assignment in + the next successor block. */ +int next_succ (struct A *p, const struct A *q, int i, int j) +{ + /* Same as above but with a nested conditionals with else clauses. */ + if (i < 0) + { + if (j < 0) + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + } + else + __builtin_strcpy (p->a, q->a); + + p->a[sizeof p->a - 1] = 0; + return 0; +} + + +int next_succ_1 (struct A *p, const struct A *q, int i, int j) +{ + /* Same as above but with a nested conditionals with else clauses. */ + if (i < 0) + { + if (j < 0) + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + else + strncpy (p->a, q->a, sizeof p->a - 2); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + } + + p->a[sizeof p->a - 2] = 0; + return 1; +} + + +int next_succ_2 (struct A *p, const struct A *q, int i, int j) +{ + /* Same as above but with a nested conditionals with else clauses. */ + if (i < 0) + { + if (j < 0) + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + else + strncpy (p->a, q->a, sizeof p->a - 2); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + } + else + __builtin_strcpy (p->a, q->a); + + p->a[sizeof p->a - 2] = 0; + return 2; +} + + +void cond_succ_warn (struct A *p, const struct A *q, int i) +{ + /* Verify that a conditional assignment doesn't suppress the warning. */ + strncpy (p->a, q->a, sizeof p->a - 1); /* { dg-warning "\\\[-Wstringop-truncation" } */ + + if (i < 0) + p->a[sizeof p->a - 1] = 0; +} + +void cond_succ_nowarn (struct A *p, const struct A *q) +{ + /* Verify that distinct but provably equivalent conditionals are + recognized and don't trigger the warning. */ + if (p != q) + strncpy (p->a, q->a, sizeof p->a - 1); + + if (p->a != q->a) + p->a[sizeof p->a - 1] = 0; +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 1266f399373..72f6a17cd32 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -1856,8 +1856,33 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) avoid the truncation warning. */ gsi_next_nondebug (&gsi); gimple *next_stmt = gsi_stmt (gsi); + if (!next_stmt) + { + /* When there is no statement in the same basic block check + the immediate successor block. */ + if (basic_block bb = gimple_bb (stmt)) + { + if (single_succ_p (bb)) + { + /* For simplicity, ignore blocks with multiple outgoing + edges for now and only consider successor blocks along + normal edges. */ + edge e = EDGE_SUCC (bb, 0); + if (!(e->flags & EDGE_ABNORMAL)) + { + gsi = gsi_start_bb (e->dest); + next_stmt = gsi_stmt (gsi); + if (next_stmt && is_gimple_debug (next_stmt)) + { + gsi_next_nondebug (&gsi); + next_stmt = gsi_stmt (gsi); + } + } + } + } + } - if (!gsi_end_p (gsi) && is_gimple_assign (next_stmt)) + if (next_stmt && is_gimple_assign (next_stmt)) { tree lhs = gimple_assign_lhs (next_stmt); tree_code code = TREE_CODE (lhs); -- 2.30.2