diff options
author | Martin Sebor <msebor@redhat.com> | 2017-12-16 23:58:34 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2017-12-16 16:58:34 -0700 |
commit | cc8bea0aeeeeb6ed8046ede1a577ee681da2ca6a (patch) | |
tree | 8ab48ffcd2c1abce152c9686d64db508a4f40a96 /gcc/tree-ssa-strlen.c | |
parent | d43568222a4564e22a6ffd370481e11ba031b318 (diff) | |
download | gcc-cc8bea0aeeeeb6ed8046ede1a577ee681da2ca6a.zip gcc-cc8bea0aeeeeb6ed8046ede1a577ee681da2ca6a.tar.gz gcc-cc8bea0aeeeeb6ed8046ede1a577ee681da2ca6a.tar.bz2 |
PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
gcc/c-family/ChangeLog:
PR tree-optimization/78918
* c-common.c (check_function_restrict): Avoid checking built-ins.
* c.opt (-Wrestrict): Include in -Wall.
gcc/ChangeLog:
PR tree-optimization/78918
* Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
* builtins.c (check_sizes): Rename...
(check_access): ...to this. Rename function arguments for clarity.
(check_memop_sizes): Adjust names.
(expand_builtin_memchr, expand_builtin_memcpy): Same.
(expand_builtin_memmove, expand_builtin_mempcpy): Same.
(expand_builtin_strcat, expand_builtin_stpncpy): Same.
(check_strncat_sizes, expand_builtin_strncat): Same.
(expand_builtin_strncpy, expand_builtin_memset): Same.
(expand_builtin_bzero, expand_builtin_memcmp): Same.
(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
(maybe_emit_sprintf_chk_warning): Same.
(expand_builtin_strcpy): Adjust.
(expand_builtin_stpcpy): Same.
(expand_builtin_with_bounds): Detect out-of-bounds accesses
in pointer-checking forms of memcpy, memmove, and mempcpy.
(gcall_to_tree_minimal, max_object_size): Define new functions.
* builtins.h (max_object_size): Declare.
* calls.c (alloc_max_size): Call max_object_size instead of
hardcoding ssizetype limit.
(get_size_range): Handle new argument.
* calls.h (get_size_range): Add a new argument.
* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
* doc/invoke.texi (-Wrestrict): Adjust, add example.
* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
operations.
(gimple_fold_builtin_memory_chk): Same.
(gimple_fold_builtin_stxcpy_chk): New function.
* gimple-ssa-warn-restrict.c: New source.
* gimple-ssa-warn-restrict.h: New header.
* gimple.c (gimple_build_call_from_tree): Propagate location.
* passes.def (pass_warn_restrict): Add new pass.
* tree-pass.h (make_pass_warn_restrict): Declare.
* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
operations.
(handle_builtin_strcat): Same.
(strlen_optimize_stmt): Rename...
(strlen_check_and_optimize_stmt): ...to this. Handle strncat,
stpncpy, strncpy, and their checking forms.
gcc/testsuite/ChangeLog:
PR tree-optimization/78918
* c-c++-common/Warray-bounds.c: New test.
* c-c++-common/Warray-bounds-2.c: New test.
* c-c++-common/Warray-bounds-3.c: New test.
* c-c++-common/Warray-bounds-4.c: New test.
* c-c++-common/Warray-bounds-5.c: New test.
* c-c++-common/Wrestrict-2.c: New test.
* c-c++-common/Wrestrict.c: New test.
* c-c++-common/Wrestrict.s: New test.
* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
* gcc.dg/range.h: New header.
* gcc.dg/memcpy-6.c: New test.
* gcc.dg/pr69172.c: Adjust.
* gcc.dg/pr79223.c: Same.
* gcc.dg/pr81345.c: Adjust.
* gcc.dg/Wobjsize-1.c: Same.
* gcc.dg/Wrestrict-2.c: New test.
* gcc.dg/Wrestrict.c: New test.
* gcc.dg/Wsizeof-pointer-memaccess1.c: Adjust.
* gcc.dg/builtin-stpncpy.c: Same.
* gcc.dg/builtin-stringop-chk-1.c: Same.
* gcc.target/i386/chkp-stropt-17.c: New test.
* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
From-SVN: r255755
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r-- | gcc/tree-ssa-strlen.c | 234 |
1 files changed, 182 insertions, 52 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 94f20ef..e75d133 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "ssa.h" #include "cgraph.h" #include "gimple-pretty-print.h" +#include "gimple-ssa-warn-restrict.h" #include "fold-const.h" #include "stor-layout.h" #include "gimple-fold.h" @@ -173,6 +174,7 @@ struct laststmt_struct } laststmt; static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree); +static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *); /* Return: @@ -1386,7 +1388,7 @@ static void handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) { int idx, didx; - tree src, dst, srclen, len, lhs, args, type, fn, oldlen; + tree src, dst, srclen, len, lhs, type, fn, oldlen; bool success; gimple *stmt = gsi_stmt (*gsi); strinfo *si, *dsi, *olddsi, *zsi; @@ -1502,6 +1504,23 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) } } dsi->stmt = stmt; + + /* Try to detect overlap before returning. This catches cases + like strcpy (d, d + n) where n is non-constant whose range + is such that (n <= strlen (d) holds). + + OLDDSI->NONZERO_chars may have been reset by this point with + oldlen holding it original value. */ + if (olddsi && oldlen) + { + /* Add 1 for the terminating NUL. */ + tree type = TREE_TYPE (oldlen); + oldlen = fold_build2 (PLUS_EXPR, type, oldlen, + build_int_cst (type, 1)); + check_bounds_or_overlap (as_a <gcall *>(stmt), olddsi->ptr, src, + oldlen, NULL_TREE); + } + return; } @@ -1574,14 +1593,32 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) if (zsi != NULL) zsi->dont_invalidate = true; - if (fn == NULL_TREE) - return; - - args = TYPE_ARG_TYPES (TREE_TYPE (fn)); - type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + if (fn) + { + tree args = TYPE_ARG_TYPES (TREE_TYPE (fn)); + type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + } + else + type = size_type_node; len = fold_convert_loc (loc, type, unshare_expr (srclen)); len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1)); + + /* Set the no-warning bit on the transformed statement? */ + bool set_no_warning = false; + + if (const strinfo *chksi = olddsi ? olddsi : dsi) + if (si + && !check_bounds_or_overlap (as_a <gcall *>(stmt), chksi->ptr, si->ptr, + NULL_TREE, len)) + { + gimple_set_no_warning (stmt, true); + set_no_warning = true; + } + + if (fn == NULL_TREE) + return; + len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true, GSI_SAME_STMT); if (dump_file && (dump_flags & TDF_DETAILS) != 0) @@ -1629,6 +1666,21 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) } else if (dump_file && (dump_flags & TDF_DETAILS) != 0) fprintf (dump_file, "not possible.\n"); + + if (set_no_warning) + gimple_set_no_warning (stmt, true); +} + +/* Check the size argument to the built-in forms of stpncpy and strncpy + for out-of-bounds offsets or overlapping access, and to see if the + size argument is derived from a call to strlen() on the source argument, + and if so, issue an appropriate warning. */ + +static void +handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi) +{ + /* Same as stxncpy(). */ + handle_builtin_stxncpy (bcode, gsi); } /* Return true if LEN depends on a call to strlen(SRC) in an interesting @@ -1909,9 +1961,10 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) return false; } -/* Check the size argument to the built-in forms of stpncpy and strncpy - to see if it's derived from calling strlen() on the source argument - and if so, issue a warning. */ +/* Check the arguments to the built-in forms of stpncpy and strncpy for + out-of-bounds offsets or overlapping access, and to see if the size + is derived from calling strlen() on the source argument, and if so, + issue the appropriate warning. */ static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) @@ -1923,8 +1976,51 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) bool with_bounds = gimple_call_with_bounds_p (stmt); + tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0); tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1); tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2); + tree dstsize = NULL_TREE, srcsize = NULL_TREE; + + int didx = get_stridx (dst); + if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL) + { + /* Compute the size of the destination string including the NUL. */ + if (sidst->nonzero_chars) + { + tree type = TREE_TYPE (sidst->nonzero_chars); + dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars, + build_int_cst (type, 1)); + } + dst = sidst->ptr; + } + + int sidx = get_stridx (src); + strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL; + if (sisrc) + { + /* strncat() and strncpy() can modify the source string by writing + over the terminating nul so SISRC->DONT_INVALIDATE must be left + clear. */ + + /* Compute the size of the source string including the NUL. */ + if (sisrc->nonzero_chars) + { + tree type = TREE_TYPE (sisrc->nonzero_chars); + srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars, + build_int_cst (type, 1)); + } + + src = sisrc->ptr; + } + else + srcsize = NULL_TREE; + + if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, src, + dstsize, srcsize)) + { + gimple_set_no_warning (stmt, true); + return; + } /* If the length argument was computed from strlen(S) for some string S retrieve the strinfo index for the string (PSS->FIRST) alonng with @@ -1938,13 +2034,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) return; } - int sidx = get_stridx (src); - strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL; - - /* strncat() and strncpy() can modify the source string by writing - over the terminating nul so SISRC->DONT_INVALIDATE must be left - clear. */ - /* Retrieve the strinfo data for the string S that LEN was computed from as some function F of strlen (S) (i.e., LEN need not be equal to strlen(S)). */ @@ -1981,17 +2070,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) } } -/* Check the size argument to the built-in forms of strncat to see if - it's derived from calling strlen() on the source argument and if so, - issue a warning. */ - -static void -handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi) -{ - /* Same as stxncpy(). */ - handle_builtin_stxncpy (bcode, gsi); -} - /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call. If strlen of the second argument is known and length of the third argument is that plus one, strlen of the first argument is the same after this @@ -2172,16 +2250,22 @@ static void handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) { int idx, didx; - tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr; + tree srclen, args, type, fn, objsz, endptr; bool success; gimple *stmt = gsi_stmt (*gsi); strinfo *si, *dsi; - location_t loc; + location_t loc = gimple_location (stmt); bool with_bounds = gimple_call_with_bounds_p (stmt); - src = gimple_call_arg (stmt, with_bounds ? 2 : 1); - dst = gimple_call_arg (stmt, 0); - lhs = gimple_call_lhs (stmt); + tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1); + tree dst = gimple_call_arg (stmt, 0); + + /* Bail if the source is the same as destination. It will be diagnosed + elsewhere. */ + if (operand_equal_p (src, dst, 0)) + return; + + tree lhs = gimple_call_lhs (stmt); didx = get_stridx (dst); if (didx < 0) @@ -2190,10 +2274,48 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) dsi = NULL; if (didx > 0) dsi = get_strinfo (didx); + + srclen = NULL_TREE; + si = NULL; + idx = get_stridx (src); + if (idx < 0) + srclen = build_int_cst (size_type_node, ~idx); + else if (idx > 0) + { + si = get_strinfo (idx); + if (si != NULL) + srclen = get_string_length (si); + } + + /* Set the no-warning bit on the transformed statement? */ + bool set_no_warning = false; + if (dsi == NULL || get_string_length (dsi) == NULL_TREE) { + { + /* The concatenation always involves copying at least one byte + (the terminating nul), even if the source string is empty. + If the source is unknown assume it's one character long and + used that as both sizes. */ + tree slen = srclen; + if (slen) + { + tree type = TREE_TYPE (slen); + slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1)); + } + + tree sptr = si && si->ptr ? si->ptr : src; + + if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr, + NULL_TREE, slen)) + { + gimple_set_no_warning (stmt, true); + set_no_warning = true; + } + } + /* strcat (p, q) can be transformed into - tmp = p + strlen (p); endptr = strpcpy (tmp, q); + tmp = p + strlen (p); endptr = stpcpy (tmp, q); with length endptr - p if we need to compute the length later on. Don't do this transformation if we don't need it. */ @@ -2226,20 +2348,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) return; } - srclen = NULL_TREE; - si = NULL; - idx = get_stridx (src); - if (idx < 0) - srclen = build_int_cst (size_type_node, ~idx); - else if (idx > 0) - { - si = get_strinfo (idx); - if (si != NULL) - srclen = get_string_length (si); - } - - loc = gimple_location (stmt); - dstlen = dsi->nonzero_chars; + tree dstlen = dsi->nonzero_chars; endptr = dsi->endptr; dsi = unshare_strinfo (dsi); @@ -2300,7 +2409,25 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) if (fn == NULL_TREE) return; - len = NULL_TREE; + if (dsi && dstlen) + { + tree type = TREE_TYPE (dstlen); + + /* Compute the size of the source sequence, including the nul. */ + tree srcsize = srclen ? srclen : size_zero_node; + srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1)); + + tree sptr = si && si->ptr ? si->ptr : src; + + if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr, + dstlen, srcsize)) + { + gimple_set_no_warning (stmt, true); + set_no_warning = true; + } + } + + tree len = NULL_TREE; if (srclen != NULL_TREE) { args = TYPE_ARG_TYPES (TREE_TYPE (fn)); @@ -2375,6 +2502,9 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) } else if (dump_file && (dump_flags & TDF_DETAILS) != 0) fprintf (dump_file, "not possible.\n"); + + if (set_no_warning) + gimple_set_no_warning (stmt, true); } /* Handle a call to malloc or calloc. */ @@ -2866,11 +2996,11 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt) } } -/* Attempt to optimize a single statement at *GSI using string length - knowledge. */ +/* Attempt to check for validity of the performed access a single statement + at *GSI using string length knowledge, and to optimize it. */ static bool -strlen_optimize_stmt (gimple_stmt_iterator *gsi) +strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi) { gimple *stmt = gsi_stmt (*gsi); @@ -3146,7 +3276,7 @@ strlen_dom_walker::before_dom_children (basic_block bb) /* Attempt to optimize individual statements. */ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); ) - if (strlen_optimize_stmt (&gsi)) + if (strlen_check_and_optimize_stmt (&gsi)) gsi_next (&gsi); bb->aux = stridx_to_strinfo; |