diff options
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; |