From daa94de24b9afdf21c636532e53561aa3713910b Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Fri, 4 Oct 2019 21:29:41 +0000 Subject: [PATCH] PR middle-end/91977 - missing -Wstringop-overflow on memcpy into a pointer plus offset gcc/ChangeLog: PR middle-end/91977 * tree-ssa-strlen.c (count_nonzero_bytes): Handle assignments with MEM_REF right operand. Avoid failing for MEM_REF assignments from uninitialized objects. gcc/testsuite/ChangeLog: PR middle-end/91977 * gcc.dg/Wstringop-overflow-18.c: New test. From-SVN: r276603 --- gcc/ChangeLog | 7 + gcc/testsuite/ChangeLog | 5 + gcc/testsuite/gcc.dg/Warray-bounds-18.c | 2 +- gcc/testsuite/gcc.dg/Wstringop-overflow-18.c | 239 +++++++++++++++++++ gcc/tree-ssa-strlen.c | 75 +++--- 5 files changed, 297 insertions(+), 31 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-18.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6a1c17c0465..18b4f89bb28 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2019-10-04 Martin Sebor + + PR middle-end/91977 + * tree-ssa-strlen.c (count_nonzero_bytes): Handle assignments with + MEM_REF right operand. Avoid failing for MEM_REF assignments from + uninitialized objects. + 2019-10-04 Martin Sebor * builtins.c (compute_objsize): Add an argument. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9a5adf787dd..d8987cabc3d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2019-10-04 Martin Sebor + + PR middle-end/91977 + * gcc.dg/Wstringop-overflow-18.c: New test. + 2019-10-04 Martin Sebor * gcc.dg/Wstringop-overflow-17.c: New test. diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-18.c b/gcc/testsuite/gcc.dg/Warray-bounds-18.c index 888fb80e7b9..092363aea31 100644 --- a/gcc/testsuite/gcc.dg/Warray-bounds-18.c +++ b/gcc/testsuite/gcc.dg/Warray-bounds-18.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -Warray-bounds" } */ +/* { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */ typedef struct { diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-18.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-18.c new file mode 100644 index 00000000000..7866b585b84 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-18.c @@ -0,0 +1,239 @@ +/* PR middle-end/91977 - missing -Wstringop-overflow on memcpy into + a pointer plus offset + { dg-do compile } + { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */ + +#define NOIPA __attribute__ ((noipa)) +#define CONCAT(a, b) a ## b +#define CAT(a, b) CONCAT (a, b) + +#define S3 "123" +#define S4 "1234" + +char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8]; +char b1[1], b2[2], b3[3], b4[4], b5[5], b6[6], b7[7], b8[8]; + +#define T(dst, src, off, n) \ + NOIPA void CAT (test_on_line_, __LINE__) (const void *s) \ + { \ + __builtin_memcpy (dst + off, src, n); \ + } typedef void dummy_type + +T (a4, s, 0, 1); +T (a4, s, 1, 1); +T (a4, s, 2, 1); +T (a4, s, 3, 1); +T (a4, s, 4, 1); // { dg-warning "writing 1 byte into a region of size 0" } + +T (a4, s, 0, 2); +T (a4, s, 1, 2); +T (a4, s, 2, 2); +T (a4, s, 3, 2); // { dg-warning "writing 2 bytes into a region of size 1" } +T (a4, s, 4, 2); // { dg-warning "writing 2 bytes into a region of size 0" } + +T (a4, s, 0, 3); +T (a4, s, 1, 3); +T (a4, s, 2, 3); // { dg-warning "writing 3 bytes into a region of size 2" } +T (a4, s, 3, 3); // { dg-warning "writing 3 bytes into a region of size 1" } +T (a4, s, 4, 3); // { dg-warning "writing 3 bytes into a region of size 0" } + +T (a4, s, 0, 4); +T (a4, s, 1, 4); // { dg-warning "writing 4 bytes into a region of size 3" } +T (a4, s, 2, 4); // { dg-warning "writing 4 bytes into a region of size 2" } +T (a4, s, 3, 4); // { dg-warning "writing 4 bytes into a region of size 1" } +T (a4, s, 4, 4); // { dg-warning "writing 4 bytes into a region of size 0" } + +T (a7, s, 3, 3); +T (a7, s, 4, 3); +T (a7, s, 5, 3); // { dg-warning "writing 3 bytes into a region of size 2" } +T (a7, s, 6, 3); // { dg-warning "writing 3 bytes into a region of size 1" } +T (a7, s, 7, 3); // { dg-warning "writing 3 bytes into a region of size 0" } + +T (a7, s, 3, 4); +T (a7, s, 4, 4); // { dg-warning "writing 4 bytes into a region of size 3" } +T (a7, s, 5, 4); // { dg-warning "writing 4 bytes into a region of size 2" } +T (a7, s, 6, 4); // { dg-warning "writing 4 bytes into a region of size 1" } +T (a7, s, 7, 4); // { dg-warning "writing 4 bytes into a region of size 0" } + +T (a7, s, 1, 6); +T (a7, s, 2, 6); // { dg-warning "writing 6 bytes into a region of size 5" } +T (a7, s, 3, 6); // { dg-warning "writing 6 bytes into a region of size 4" } +T (a7, s, 4, 6); // { dg-warning "writing 6 bytes into a region of size 3" } +T (a7, s, 5, 6); // { dg-warning "writing 6 bytes into a region of size 2" } +T (a7, s, 6, 6); // { dg-warning "writing 6 bytes into a region of size 1" } +T (a7, s, 7, 6); // { dg-warning "writing 6 bytes into a region of size 0" } + +T (a8, s, 1, 7); +T (a8, s, 2, 7); // { dg-warning "writing 7 bytes into a region of size 6" } +T (a8, s, 3, 7); // { dg-warning "writing 7 bytes into a region of size 5" } +T (a8, s, 4, 7); // { dg-warning "writing 7 bytes into a region of size 4" } +T (a8, s, 5, 7); // { dg-warning "writing 7 bytes into a region of size 3" } +T (a8, s, 6, 7); // { dg-warning "writing 7 bytes into a region of size 2" } +T (a8, s, 7, 7); // { dg-warning "writing 7 bytes into a region of size 1" } +T (a8, s, 8, 7); // { dg-warning "writing 7 bytes into a region of size 0" } + +#undef T +#define T(dst, src, off, n) \ + NOIPA void CAT (test_on_line_, __LINE__) (const void *s) \ + { \ + char *d = dst + off; \ + __builtin_memcpy (d, src, n); \ + } typedef void dummy_type + +T (a4, s, 0, 1); +T (a4, s, 1, 1); +T (a4, s, 2, 1); +T (a4, s, 3, 1); +T (a4, s, 4, 1); // { dg-warning "writing 1 byte into a region of size 0" } + +T (a4, s, 0, 2); +T (a4, s, 1, 2); +T (a4, s, 2, 2); +T (a4, s, 3, 2); // { dg-warning "writing 2 bytes into a region of size 1" } +T (a4, s, 4, 2); // { dg-warning "writing 2 bytes into a region of size 0" } + +T (a4, s, 0, 3); +T (a4, s, 1, 3); +T (a4, s, 2, 3); // { dg-warning "writing 3 bytes into a region of size 2" } +T (a4, s, 3, 3); // { dg-warning "writing 3 bytes into a region of size 1" } +T (a4, s, 4, 3); // { dg-warning "writing 3 bytes into a region of size 0" } + +T (a4, s, 0, 4); +T (a4, s, 1, 4); // { dg-warning "writing 4 bytes into a region of size 3" } +T (a4, s, 2, 4); // { dg-warning "writing 4 bytes into a region of size 2" } +T (a4, s, 3, 4); // { dg-warning "writing 4 bytes into a region of size 1" } +T (a4, s, 4, 4); // { dg-warning "writing 4 bytes into a region of size 0" } + +T (a7, s, 3, 3); +T (a7, s, 4, 3); +T (a7, s, 5, 3); // { dg-warning "writing 3 bytes into a region of size 2" } +T (a7, s, 6, 3); // { dg-warning "writing 3 bytes into a region of size 1" } +T (a7, s, 7, 3); // { dg-warning "writing 3 bytes into a region of size 0" } + +T (a7, s, 3, 4); +T (a7, s, 4, 4); // { dg-warning "writing 4 bytes into a region of size 3" } +T (a7, s, 5, 4); // { dg-warning "writing 4 bytes into a region of size 2" } +T (a7, s, 6, 4); // { dg-warning "writing 4 bytes into a region of size 1" } +T (a7, s, 7, 4); // { dg-warning "writing 4 bytes into a region of size 0" } + +T (a7, s, 1, 6); +T (a7, s, 2, 6); // { dg-warning "writing 6 bytes into a region of size 5" } +T (a7, s, 3, 6); // { dg-warning "writing 6 bytes into a region of size 4" } +T (a7, s, 4, 6); // { dg-warning "writing 6 bytes into a region of size 3" } +T (a7, s, 5, 6); // { dg-warning "writing 6 bytes into a region of size 2" } +T (a7, s, 6, 6); // { dg-warning "writing 6 bytes into a region of size 1" } +T (a7, s, 7, 6); // { dg-warning "writing 6 bytes into a region of size 0" } + +#undef T +#define T(dst, src, init, off, n) \ + NOIPA void CAT (test_on_line_, __LINE__) (void) \ + { \ + __builtin_strcpy (src, init); \ + char *d = dst + off; \ + __builtin_memcpy (d, src, n); \ + } typedef void dummy_type + + +T (a6, b6, S4, 0, 4); +T (a6, b6, S4, 1, 4); +T (a6, b6, S4, 2, 4); +T (a6, b6, S4, 3, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a6, b6, S4, 4, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a6, b6, S4, 5, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a6, b6, S4, 6, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a7, b7, S4, 0, 4); +T (a7, b7, S4, 1, 4); +T (a7, b7, S4, 2, 4); +T (a7, b7, S4, 3, 4); +T (a7, b7, S4, 4, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a7, b7, S4, 5, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a7, b7, S4, 6, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a7, b7, S4, 7, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a8, b4, S3, 0, 4); +T (a8, b4, S3, 1, 4); +T (a8, b4, S3, 2, 4); +T (a8, b4, S3, 3, 4); +T (a8, b4, S3, 4, 4); +T (a8, b4, S3, 5, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a8, b4, S3, 6, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a8, b4, S3, 7, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a8, b4, S3, 8, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a8, b8, S3, 0, 4); +T (a8, b8, S3, 1, 4); +T (a8, b8, S3, 2, 4); +T (a8, b8, S3, 3, 4); +T (a8, b8, S3, 4, 4); +T (a8, b8, S3, 5, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a8, b8, S3, 6, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a8, b8, S3, 7, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a8, b8, S3, 8, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a8, b8, S4, 0, 4); +T (a8, b8, S4, 1, 4); +T (a8, b8, S4, 2, 4); +T (a8, b8, S4, 3, 4); +T (a8, b8, S4, 4, 4); +T (a8, b8, S4, 5, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a8, b8, S4, 6, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a8, b8, S4, 7, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a8, b8, S4, 8, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a8, b8, S4, 0, 5); +T (a8, b8, S4, 1, 5); +T (a8, b8, S4, 2, 5); +T (a8, b8, S4, 3, 5); +T (a8, b8, S4, 4, 5); // { dg-warning "writing 5 bytes into a region of size 4" } */ +T (a8, b8, S4, 5, 5); // { dg-warning "writing 5 bytes into a region of size 3" } */ +T (a8, b8, S4, 6, 5); // { dg-warning "writing 5 bytes into a region of size 2" } */ +T (a8, b8, S4, 7, 5); // { dg-warning "writing 5 bytes into a region of size 1" } */ +T (a8, b8, S4, 8, 5); // { dg-warning "writing 5 bytes into a region of size 0" } */ + +T (a8, b8, S4, 0, 6); +T (a8, b8, S4, 1, 6); +T (a8, b8, S4, 2, 6); +T (a8, b8, S4, 3, 6); // { dg-warning "writing 6 bytes into a region of size 5" } */ +T (a8, b8, S4, 4, 6); // { dg-warning "writing 6 bytes into a region of size 4" } */ +T (a8, b8, S4, 5, 6); // { dg-warning "writing 6 bytes into a region of size 3" } */ +T (a8, b8, S4, 6, 6); // { dg-warning "writing 6 bytes into a region of size 2" } */ +T (a8, b8, S4, 7, 6); // { dg-warning "writing 6 bytes into a region of size 1" } */ +T (a8, b8, S4, 8, 6); // { dg-warning "writing 6 bytes into a region of size 0" } */ + + +#undef T +#define T(dst, init, off, n) \ + NOIPA void CAT (test_on_line_, __LINE__) (char *src) \ + { \ + __builtin_strcpy (src, init); \ + char *d = dst + off; \ + __builtin_memcpy (d, src, n); \ + } typedef void dummy_type + +T (a6, S4, 0, 4); +T (a6, S4, 1, 4); +T (a6, S4, 2, 4); +T (a6, S4, 3, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a6, S4, 4, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a6, S4, 5, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a6, S4, 6, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a7, S4, 0, 4); +T (a7, S4, 1, 4); +T (a7, S4, 2, 4); +T (a7, S4, 3, 4); +T (a7, S4, 4, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a7, S4, 5, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a7, S4, 6, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a7, S4, 7, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ + +T (a8, S3, 0, 4); +T (a8, S3, 1, 4); +T (a8, S3, 2, 4); +T (a8, S3, 3, 4); +T (a8, S3, 4, 4); +T (a8, S3, 5, 4); // { dg-warning "writing 4 bytes into a region of size 3" } */ +T (a8, S3, 6, 4); // { dg-warning "writing 4 bytes into a region of size 2" } */ +T (a8, S3, 7, 4); // { dg-warning "writing 4 bytes into a region of size 1" } */ +T (a8, S3, 8, 4); // { dg-warning "writing 4 bytes into a region of size 0" } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index c82bc7c4815..d68df19aa82 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -3801,40 +3801,44 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset, tree type = TREE_TYPE (exp); if (TREE_CODE (type) == INTEGER_TYPE && TYPE_MODE (type) == TYPE_MODE (char_type_node) - && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node)) + && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node) + && tree_expr_nonzero_p (exp)) { - /* Determine if the character EXP is known to be non-zero - (even if its exact value is not known) and if so, recurse - once to set the range, etc. */ - if (tree_expr_nonzero_p (exp)) - return count_nonzero_bytes (build_int_cst (type, 1), - offset, nbytes, lenrange, - nulterm, allnul, allnonnul, snlim); - /* Don't know whether EXP is or isn't nonzero. */ - return false; + /* If the character EXP is known to be non-zero (even if its + exact value is not known) recurse once to set the range + for an arbitrary constant. */ + exp = build_int_cst (type, 1); + return count_nonzero_bytes (exp, offset, 1, lenrange, + nulterm, allnul, allnonnul, snlim); } gimple *stmt = SSA_NAME_DEF_STMT (exp); - if (gimple_code (stmt) != GIMPLE_PHI) - return false; - - /* Avoid processing an SSA_NAME that has already been visited - or if an SSA_NAME limit has been reached. Indicate success - if the former and failure if the latter. */ - if (int res = snlim.next_ssa_name (exp)) - return res > 0; - - /* Determine the minimum and maximum from the PHI arguments. */ - unsigned int n = gimple_phi_num_args (stmt); - for (unsigned i = 0; i != n; i++) + if (gimple_assign_single_p (stmt)) { - tree def = gimple_phi_arg_def (stmt, i); - if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm, - allnul, allnonnul, snlim)) + exp = gimple_assign_rhs1 (stmt); + if (TREE_CODE (exp) != MEM_REF) return false; } + else if (gimple_code (stmt) == GIMPLE_PHI) + { + /* Avoid processing an SSA_NAME that has already been visited + or if an SSA_NAME limit has been reached. Indicate success + if the former and failure if the latter. */ + if (int res = snlim.next_ssa_name (exp)) + return res > 0; + + /* Determine the minimum and maximum from the PHI arguments. */ + unsigned int n = gimple_phi_num_args (stmt); + for (unsigned i = 0; i != n; i++) + { + tree def = gimple_phi_arg_def (stmt, i); + if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm, + allnul, allnonnul, snlim)) + return false; + } - return true; + return true; + } } if (TREE_CODE (exp) == MEM_REF) @@ -3897,14 +3901,25 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset, prep = reinterpret_cast (buf); /* Try to extract the representation of the constant object or expression starting from the offset. */ - nbytes = native_encode_expr (exp, buf, sizeof buf, offset); - if (!nbytes) - return false; + unsigned repsize = native_encode_expr (exp, buf, sizeof buf, offset); + if (repsize < nbytes) + { + /* This should only happen when REPSIZE is zero because EXP + doesn't denote an object with a known initializer, except + perhaps when the reference reads past its end. */ + lenrange[0] = 0; + prep = NULL; + } + else + nbytes = repsize; } + if (!nbytes) + return false; + /* Compute the number of leading nonzero bytes in the representation and update the minimum and maximum. */ - unsigned n = strnlen (prep, nbytes); + unsigned n = prep ? strnlen (prep, nbytes) : nbytes; if (n < lenrange[0]) lenrange[0] = n; -- 2.30.2