From 6a33d0ff21e941fc3a65f23a753cc318aaae82b5 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Tue, 21 Nov 2017 20:01:58 +0000 Subject: [PATCH] PR tree-optimization/82945 - add warning for passing non-strings to functions that expect string arguments gcc/ChangeLog: PR tree-optimization/82945 * builtins.c (expand_builtin_strlen): Call maybe_warn_nonstring_arg. * calls.h (maybe_warn_nonstring_arg): Declare new function. * calls.c (get_attr_nonstring_decl, maybe_warn_nonstring_arg): New functions. (initialize_argument_information): Call maybe_warn_nonstring_arg. * calls.h (get_attr_nonstring_decl): Declare new function. * doc/extend.texi (attribute nonstring): Update. * gimple-fold.c (gimple_fold_builtin_strncpy): Call get_attr_nonstring_decl and handle it. * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Same. Improve detection of nul-termination. (strlen_to_stridx): Change to a pointer. (handle_builtin_strlen, handle_builtin_stxncpy): Adjust. (pass_strlen::execute): Same. gcc/testsuite/ChangeLog: PR tree-optimization/82945 * c-c++-common/Wstringop-truncation-2.c: New test. * c-c++-common/Wstringop-truncation.c: Adjust. * c-c++-common/attr-nonstring-2.c: Adjust. * c-c++-common/attr-nonstring-3.c: New test. From-SVN: r255031 --- gcc/ChangeLog | 18 + gcc/builtins.c | 5 + gcc/calls.c | 208 ++++++++ gcc/calls.h | 2 + gcc/doc/extend.texi | 25 +- gcc/gimple-fold.c | 108 ++-- gcc/testsuite/ChangeLog | 8 + .../c-c++-common/Wstringop-truncation-2.c | 105 ++++ .../c-c++-common/Wstringop-truncation.c | 20 +- gcc/testsuite/c-c++-common/attr-nonstring-2.c | 3 +- gcc/testsuite/c-c++-common/attr-nonstring-3.c | 474 ++++++++++++++++++ gcc/tree-ssa-strlen.c | 122 +++-- 12 files changed, 972 insertions(+), 126 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/Wstringop-truncation-2.c create mode 100644 gcc/testsuite/c-c++-common/attr-nonstring-3.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 86b538479ec..2a4e92fb751 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2017-11-21 Martin Sebor + + PR tree-optimization/82945 + * builtins.c (expand_builtin_strlen): Call maybe_warn_nonstring_arg. + * calls.h (maybe_warn_nonstring_arg): Declare new function. + * calls.c (get_attr_nonstring_decl, maybe_warn_nonstring_arg): New + functions. + (initialize_argument_information): Call maybe_warn_nonstring_arg. + * calls.h (get_attr_nonstring_decl): Declare new function. + * doc/extend.texi (attribute nonstring): Update. + * gimple-fold.c (gimple_fold_builtin_strncpy): Call + get_attr_nonstring_decl and handle it. + * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Same. Improve + detection of nul-termination. + (strlen_to_stridx): Change to a pointer. + (handle_builtin_strlen, handle_builtin_stxncpy): Adjust. + (pass_strlen::execute): Same. + 2017-11-21 Sergey Shalnov * config/i386/i386-opts.h (enum prefer_vector_width): Added new enum diff --git a/gcc/builtins.c b/gcc/builtins.c index 650de0d9aca..b9744592017 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -2885,6 +2885,11 @@ expand_builtin_strlen (tree exp, rtx target, if (!maybe_expand_insn (icode, 4, ops)) return NULL_RTX; + /* Check to see if the argument was declared attribute nonstring + and if so, issue a warning since at this point it's not known + to be nul-terminated. */ + maybe_warn_nonstring_arg (TREE_OPERAND (CALL_EXPR_FN (exp), 0), exp); + /* Now that we are assured of success, expand the source. */ start_sequence (); pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL); diff --git a/gcc/calls.c b/gcc/calls.c index 3730f43c7a9..cae543c481f 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1494,6 +1494,210 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) } } +/* If EXPR refers to a character array or pointer declared attribute + nonstring return a decl for that array or pointer and set *REF to + the referenced enclosing object or pointer. Otherwise returns + null. */ + +tree +get_attr_nonstring_decl (tree expr, tree *ref) +{ + tree decl = expr; + if (TREE_CODE (decl) == SSA_NAME) + { + gimple *def = SSA_NAME_DEF_STMT (decl); + + if (is_gimple_assign (def)) + { + tree_code code = gimple_assign_rhs_code (def); + if (code == ADDR_EXPR + || code == COMPONENT_REF + || code == VAR_DECL) + decl = gimple_assign_rhs1 (def); + } + else if (tree var = SSA_NAME_VAR (decl)) + decl = var; + } + + if (TREE_CODE (decl) == ADDR_EXPR) + decl = TREE_OPERAND (decl, 0); + + if (ref) + *ref = decl; + + if (TREE_CODE (decl) == COMPONENT_REF) + decl = TREE_OPERAND (decl, 1); + + if (DECL_P (decl) + && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl))) + return decl; + + return NULL_TREE; +} + +/* Check the size argument to the strncmp built-in to see if it's within + the bounds of the arguments and if not, issue a warning. */ + +static void +warn_nonstring_bound (tree fndecl, tree call) +{ + bool with_bounds = CALL_WITH_BOUNDS_P (call); + + tree cnt = CALL_EXPR_ARG (call, with_bounds ? 4 : 2); + + tree cntrange[2]; + if (!get_size_range (cnt, cntrange)) + return; + + location_t callloc = EXPR_LOCATION (call); + + for (unsigned i = 0; i != 2; ++i) + { + tree str = CALL_EXPR_ARG (call, i + 2 * with_bounds); + + tree sref; + tree decl = get_attr_nonstring_decl (str, &sref); + if (!decl) + continue; + + tree type = TREE_TYPE (decl); + if (TREE_CODE (type) != ARRAY_TYPE) + continue; + + tree dom = TYPE_DOMAIN (type); + if (!dom) + continue; + + tree bound = TYPE_MAX_VALUE (dom); + if (!bound) + continue; + + bool warned = false; + if (tree_int_cst_le (bound, cntrange[0])) + warned = warning_at (callloc, OPT_Wstringop_truncation, + "%qD argument %i declared attribute % " + "is smaller than the specified bound %E", + fndecl, i, cntrange[0]); + if (warned) + { + location_t loc = DECL_SOURCE_LOCATION (decl); + if (loc != UNKNOWN_LOCATION) + inform (loc, "argument %qD declared here", decl); + } + } +} + +/* Warn about passing a non-string array/pointer to a function that + expects a nul-terminated string argument. */ + +void +maybe_warn_nonstring_arg (tree fndecl, tree exp) +{ + if (!fndecl || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL) + return; + + bool with_bounds = CALL_WITH_BOUNDS_P (exp); + + /* The bound argument to a bounded string function like strncpy. */ + tree bound = NULL_TREE; + + /* It's safe to call "bounded" string functions with a non-string + argument since the functions provide an explicit bound for this + purpose. */ + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_STPNCPY: + case BUILT_IN_STPNCPY_CHK: + case BUILT_IN_STRNCMP: + case BUILT_IN_STRNCASECMP: + case BUILT_IN_STRNCPY: + case BUILT_IN_STRNCPY_CHK: + bound = CALL_EXPR_ARG (exp, with_bounds ? 4 : 2); + break; + + case BUILT_IN_STRNDUP: + bound = CALL_EXPR_ARG (exp, with_bounds ? 2 : 1); + break; + + default: + break; + } + + /* Determine the range of the bound argument (if specified). */ + tree bndrng[2] = { NULL_TREE, NULL_TREE }; + if (bound) + get_size_range (bound, bndrng); + + /* Iterate over the built-in function's formal arguments and check + each const char* against the actual argument. If the actual + argument is declared attribute non-string issue a warning unless + the argument's maximum length is bounded. */ + function_args_iterator it; + function_args_iter_init (&it, TREE_TYPE (fndecl)); + + for (unsigned argno = 0; ; ++argno, function_args_iter_next (&it)) + { + tree argtype = function_args_iter_cond (&it); + if (!argtype) + break; + + if (TREE_CODE (argtype) != POINTER_TYPE) + continue; + + argtype = TREE_TYPE (argtype); + + if (TREE_CODE (argtype) != INTEGER_TYPE + || !TYPE_READONLY (argtype)) + continue; + + argtype = TYPE_MAIN_VARIANT (argtype); + if (argtype != char_type_node) + continue; + + tree callarg = CALL_EXPR_ARG (exp, argno); + if (TREE_CODE (callarg) == ADDR_EXPR) + callarg = TREE_OPERAND (callarg, 0); + + /* See if the destination is declared with attribute "nonstring". */ + tree decl = get_attr_nonstring_decl (callarg); + if (!decl) + continue; + + tree type = TREE_TYPE (decl); + + offset_int wibnd = 0; + if (bndrng[0]) + wibnd = wi::to_offset (bndrng[0]); + + offset_int asize = wibnd; + + if (TREE_CODE (type) == ARRAY_TYPE) + if (tree arrbnd = TYPE_DOMAIN (type)) + { + if ((arrbnd = TYPE_MAX_VALUE (arrbnd))) + asize = wi::to_offset (arrbnd) + 1; + } + + location_t loc = EXPR_LOCATION (exp); + + bool warned = false; + + if (wi::ltu_p (asize, wibnd)) + warned = warning_at (loc, OPT_Wstringop_overflow_, + "%qD argument %i declared attribute % " + "is smaller than the specified bound %E", + fndecl, argno + 1, bndrng[0]); + else if (!bound) + warned = warning_at (loc, OPT_Wstringop_overflow_, + "%qD argument %i declared attribute %", + fndecl, argno + 1); + + if (warned) + inform (DECL_SOURCE_LOCATION (decl), + "argument %qD declared here", decl); + } +} + /* Issue an error if CALL_EXPR was flagged as requiring tall-call optimization. */ @@ -1943,6 +2147,10 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, alloc_size. */ maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx); } + + /* Detect passing non-string arguments to functions expecting + nul-terminated strings. */ + maybe_warn_nonstring_arg (fndecl, exp); } /* Update ARGS_SIZE to contain the total size for the argument block. diff --git a/gcc/calls.h b/gcc/calls.h index df5817fe785..9b7fa9a2f9c 100644 --- a/gcc/calls.h +++ b/gcc/calls.h @@ -39,5 +39,7 @@ extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode, tree, bool); extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]); extern bool get_size_range (tree, tree[2]); +extern tree get_attr_nonstring_decl (tree, tree * = NULL); +extern void maybe_warn_nonstring_arg (tree, tree); #endif // GCC_CALLS_H diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 50bda9140f8..65fc8602860 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -6008,22 +6008,33 @@ types (@pxref{Common Function Attributes}, The @code{nonstring} variable attribute specifies that an object or member declaration with type array of @code{char} or pointer to @code{char} is intended to store character arrays that do not necessarily contain -a terminating @code{NUL} character. This is useful to avoid warnings -when such an array or pointer is used as an argument to a bounded string -manipulation function such as @code{strncpy}. For example, without the -attribute, GCC will issue a warning for the call below because it may -truncate the copy without appending the terminating NUL character. Using -the attribute makes it possible to suppress the warning. +a terminating @code{NUL} character. This is useful in detecting uses +of such arrays or pointers with functions that expect NUL-terminated +strings, and to avoid warnings when such an array or pointer is used +as an argument to a bounded string manipulation function such as +@code{strncpy}. For example, without the attribute, GCC will issue +a warning for the @code{strncpy} call below because it may truncate +the copy without appending the terminating @code{NUL} character. Using +the attribute makes it possible to suppress the warning. However, when +the array is declared with the attribute the call to @code{strlen} is +diagnosed because when the array doesn't contain a @code{NUL}-terminated +string the call is undefined. To copy, compare, of search non-string +character arrays use the @code{memcpy}, @code{memcmp}, @code{memchr}, +and other functions that operate on arrays of bytes. In addition, +calling @code{strnlen} and @code{strndup} with such arrays is safe +provided a suitable bound is specified, and not diagnosed. @smallexample struct Data @{ char name [32] __attribute__ ((nonstring)); @}; -void f (struct Data *pd, const char *s) + +int f (struct Data *pd, const char *s) @{ strncpy (pd->name, s, sizeof pd->name); @dots{} + return strlen (pd->name); // unsafe, gets a warning @} @end smallexample diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 1ed63833b73..ea8f92eab7b 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -62,6 +62,7 @@ along with GCC; see the file COPYING3. If not see #include "asan.h" #include "diagnostic-core.h" #include "intl.h" +#include "calls.h" /* Return true when DECL can be referenced from current unit. FROM_DECL (if non-null) specify constructor of variable DECL was taken from. @@ -1558,25 +1559,31 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi, { gimple *stmt = gsi_stmt (*gsi); location_t loc = gimple_location (stmt); + bool nonstring = get_attr_nonstring_decl (dest) != NULL_TREE; /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) { - tree fndecl = gimple_call_fndecl (stmt); - gcall *call = as_a (stmt); - - /* Warn about the lack of nul termination: the result is not - a (nul-terminated) string. */ - tree slen = get_maxval_strlen (src, 0); - if (slen && !integer_zerop (slen)) - warning_at (loc, OPT_Wstringop_truncation, - "%G%qD destination unchanged after copying no bytes " - "from a string of length %E", - call, fndecl, slen); - else - warning_at (loc, OPT_Wstringop_truncation, - "%G%qD destination unchanged after copying no bytes", - call, fndecl); + /* Avoid warning if the destination refers to a an array/pointer + decorate with attribute nonstring. */ + if (!nonstring) + { + tree fndecl = gimple_call_fndecl (stmt); + gcall *call = as_a (stmt); + + /* Warn about the lack of nul termination: the result is not + a (nul-terminated) string. */ + tree slen = get_maxval_strlen (src, 0); + if (slen && !integer_zerop (slen)) + warning_at (loc, OPT_Wstringop_truncation, + "%G%qD destination unchanged after copying no bytes " + "from a string of length %E", + call, fndecl, slen); + else + warning_at (loc, OPT_Wstringop_truncation, + "%G%qD destination unchanged after copying no bytes", + call, fndecl); + } replace_call_with_value (gsi, dest); return true; @@ -1601,53 +1608,36 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi, if (tree_int_cst_lt (ssize, len)) return false; - if (tree_int_cst_lt (len, slen)) - { - tree fndecl = gimple_call_fndecl (stmt); - gcall *call = as_a (stmt); - - warning_at (loc, OPT_Wstringop_truncation, - (tree_int_cst_equal (size_one_node, len) - ? G_("%G%qD output truncated copying %E byte " - "from a string of length %E") - : G_("%G%qD output truncated copying %E bytes " - "from a string of length %E")), - call, fndecl, len, slen); - } - else if (tree_int_cst_equal (len, slen)) + if (!nonstring) { - tree decl = dest; - if (TREE_CODE (decl) == SSA_NAME) + if (tree_int_cst_lt (len, slen)) { - gimple *def_stmt = SSA_NAME_DEF_STMT (decl); - if (is_gimple_assign (def_stmt)) - { - tree_code code = gimple_assign_rhs_code (def_stmt); - if (code == ADDR_EXPR || code == VAR_DECL) - decl = gimple_assign_rhs1 (def_stmt); - } + tree fndecl = gimple_call_fndecl (stmt); + gcall *call = as_a (stmt); + + warning_at (loc, OPT_Wstringop_truncation, + (tree_int_cst_equal (size_one_node, len) + ? G_("%G%qD output truncated copying %E byte " + "from a string of length %E") + : G_("%G%qD output truncated copying %E bytes " + "from a string of length %E")), + call, fndecl, len, slen); + } + else if (tree_int_cst_equal (len, slen)) + { + tree fndecl = gimple_call_fndecl (stmt); + gcall *call = as_a (stmt); + + warning_at (loc, OPT_Wstringop_truncation, + (tree_int_cst_equal (size_one_node, len) + ? G_("%G%qD output truncated before terminating nul " + "copying %E byte from a string of the same " + "length") + : G_("%G%qD output truncated before terminating nul " + "copying %E bytes from a string of the same " + "length")), + call, fndecl, len); } - - if (TREE_CODE (decl) == ADDR_EXPR) - decl = TREE_OPERAND (decl, 0); - - if (TREE_CODE (decl) == COMPONENT_REF) - decl = TREE_OPERAND (decl, 1); - - tree fndecl = gimple_call_fndecl (stmt); - gcall *call = as_a (stmt); - - if (!DECL_P (decl) - || !lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl))) - warning_at (loc, OPT_Wstringop_truncation, - (tree_int_cst_equal (size_one_node, len) - ? G_("%G%qD output truncated before terminating nul " - "copying %E byte from a string of the same " - "length") - : G_("%G%qD output truncated before terminating nul " - "copying %E bytes from a string of the same " - "length")), - call, fndecl, len); } /* OK transform into builtin memcpy. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index a793d57ff71..0d86a868534 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2017-11-21 Martin Sebor + + PR tree-optimization/82945 + * c-c++-common/Wstringop-truncation-2.c: New test. + * c-c++-common/Wstringop-truncation.c: Adjust. + * c-c++-common/attr-nonstring-2.c: Adjust. + * c-c++-common/attr-nonstring-3.c: New test. + 2017-11-21 Sergey Shalnov * g++.dg/ext/pr57362.C (__attribute__): Test diff --git a/gcc/testsuite/c-c++-common/Wstringop-truncation-2.c b/gcc/testsuite/c-c++-common/Wstringop-truncation-2.c new file mode 100644 index 00000000000..7b3c182b0fa --- /dev/null +++ b/gcc/testsuite/c-c++-common/Wstringop-truncation-2.c @@ -0,0 +1,105 @@ +/* Verify that + { dg-do compile } + { dg-options "-O2 -Wstringop-truncation -Wno-stringop-overflow -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; + +#define stpncpy(d, s, n) __builtin_stpncpy ((d), (s), (n)) +#define strncpy(d, s, n) __builtin_stpncpy ((d), (s), (n)) + +void sink (void*); + +struct A { + char arr[3] __attribute__ ((nonstring)); + char str[3]; +}; + +struct B { struct A a[3]; int i; }; +struct C { struct B b[3]; int i; }; + +void stpncpy_arr_1 (struct C *pc, const char *s) +{ + stpncpy (pc->b[0].a[0].arr, s, sizeof pc->b[0].a[0].arr); + sink (pc->b[0].a[0].arr); + + stpncpy (pc->b[0].a[1].arr, s, sizeof pc->b[0].a[1].arr); + sink (pc->b[0].a[1].arr); + + stpncpy (pc->b[0].a[2].arr, s, sizeof pc->b[0].a[2].arr); + sink (pc->b[0].a[2].arr); + + stpncpy (pc->b[1].a[0].arr, s, sizeof pc->b[1].a[0].arr); + sink (pc->b[1].a[0].arr); + + stpncpy (pc->b[1].a[1].arr, s, sizeof pc->b[1].a[1].arr); + sink (pc->b[1].a[1].arr); + + stpncpy (pc->b[1].a[2].arr, s, sizeof pc->b[1].a[2].arr); + sink (pc->b[1].a[2].arr); + + stpncpy (pc->b[2].a[0].arr, s, sizeof pc->b[2].a[0].arr); + sink (pc->b[2].a[0].arr); + + stpncpy (pc->b[2].a[1].arr, s, sizeof pc->b[2].a[1].arr); + sink (pc->b[2].a[1].arr); + + stpncpy (pc->b[2].a[2].arr, s, sizeof pc->b[2].a[2].arr); + sink (pc->b[2].a[2].arr); +} + +void stpncpy_str_nowarn_1 (struct C *pc, const char *s) +{ + stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str)[-1] = 0; /* { dg-bogus "\\\[-Wstringop-truncation" } */ +} + +void stpncpy_str_nowarn_2 (struct C *pc, const char *s) +{ + *stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str - 1) = 0; /* { dg-bogus "\\\[-Wstringop-truncation" } */ +} + +void stpncpy_str_nowarn_3 (struct C *pc, const char *s) +{ + char *d = stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + + d[-1] = 0; +} + +void stpncpy_str_nowarn_4 (struct C *pc, const char *s) +{ + char *d = stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + + *d = 0; +} + +void strncpy_arr_1 (struct C *pc, const char *s) +{ + strncpy (pc->b[0].a[0].arr, s, sizeof pc->b[0].a[0].arr); + sink (pc->b[0].a[0].arr); + + strncpy (pc->b[0].a[1].arr, s, sizeof pc->b[0].a[1].arr); + sink (pc->b[0].a[1].arr); + + strncpy (pc->b[0].a[2].arr, s, sizeof pc->b[0].a[2].arr); + sink (pc->b[0].a[2].arr); +} + +void strncpy_str_nowarn_1 (struct C *pc, const char *s) +{ + strncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-bogus "\\\[-Wstringop-truncation" } */ + + pc->b[0].a[0].str[sizeof pc->b[0].a[0].str - 1] = 0; +} + +void strncpy_str_warn_1 (struct C *pc, const char *s) +{ + strncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-warning "specified bound 3 equals destination size" } */ + + pc->b[1].a[0].str[sizeof pc->b[0].a[0].str - 1] = 0; +} + +void strncpy_str_warn_2 (struct C *pc, const char *s) +{ + strncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-warning "specified bound 3 equals destination size" } */ + + pc->b[0].a[1].str[sizeof pc->b[0].a[0].str - 1] = 0; +} diff --git a/gcc/testsuite/c-c++-common/Wstringop-truncation.c b/gcc/testsuite/c-c++-common/Wstringop-truncation.c index 7fc439fb630..dc8c618976e 100644 --- a/gcc/testsuite/c-c++-common/Wstringop-truncation.c +++ b/gcc/testsuite/c-c++-common/Wstringop-truncation.c @@ -193,35 +193,35 @@ void test_strncpy_ptr (char *d, const char* s, const char *t, int i) CPY (d, CHOOSE ("123", "12"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 2" } */ { - signed char n = strlen (s); /* { dg-message "length computed here" } */ + signed char n = strlen (s); /* { dg-message "length computed here" } */ CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ } { - short n = strlen (s); /* { dg-message "length computed here" } */ + short n = strlen (s); /* { dg-message "length computed here" } */ CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ } { - int n = strlen (s); /* { dg-message "length computed here" } */ + int n = strlen (s); /* { dg-message "length computed here" } */ CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ } { - unsigned n = strlen (s); /* { dg-message "length computed here" } */ + unsigned n = strlen (s); /* { dg-message "length computed here" } */ CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ } { size_t n; - n = strlen (s); /* { dg-message "length computed here" } */ + n = strlen (s); /* { dg-message "length computed here" } */ CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ } { size_t n; char *dp2 = d + 1; - n = strlen (s); /* { dg-message "length computed here" } */ + n = strlen (s); /* { dg-message "length computed here" } */ CPY (dp2, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ } @@ -312,9 +312,11 @@ void test_strncpy_array (Dest *pd, int i, const char* s) /* Exercise destination with attribute "nonstring". */ CPY (pd->c3ns, "", 3); CPY (pd->c3ns, "", 1); - /* Truncation is still diagnosed -- using strncpy in this case is - pointless and should be replaced with memcpy. */ - CPY (pd->c3ns, "12", 1); /* { dg-warning "output truncated copying 1 byte from a string of length 2" } */ + /* It could be argued that truncation in the literal case should be + diagnosed even for non-strings. Using strncpy in this case is + pointless and should be replaced with memcpy. But it would likely + be viewed as a false positive. */ + CPY (pd->c3ns, "12", 1); CPY (pd->c3ns, "12", 2); CPY (pd->c3ns, "12", 3); CPY (pd->c3ns, "123", 3); diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-2.c b/gcc/testsuite/c-c++-common/attr-nonstring-2.c index 6e273e785a0..67fce0375d8 100644 --- a/gcc/testsuite/c-c++-common/attr-nonstring-2.c +++ b/gcc/testsuite/c-c++-common/attr-nonstring-2.c @@ -89,7 +89,7 @@ void test_pointer (const char *s, unsigned n) strncpy (pns_1, "a", 1); strncpy (pns_2, "ab", 2); strncpy (pns_3, "abc", 3); - strncpy (pns_3, s7, 3); /* { dg-warning "output truncated copying 3 bytes from a string of length 7" } */ + strncpy (pns_3, s7, 3); strncpy (pns_1, s, 1); strncpy (pns_2, s, 1); @@ -105,6 +105,7 @@ void test_member_array (struct MemArrays *p, const char *s, unsigned n) { const char s7[] = "1234567"; + strncpy (p->ma3, "", 0); strncpy (p->ma3, "a", 1); strncpy (p->ma4, "ab", 2); strncpy (p->ma5, "abc", 3); diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-3.c b/gcc/testsuite/c-c++-common/attr-nonstring-3.c new file mode 100644 index 00000000000..1645ed3ae6d --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-3.c @@ -0,0 +1,474 @@ +/* Test to exercise warnings when an array declared with attribute "nonstring" + is passed to a function that expects a nul-terminated string as an argument. + { dg-do compile } + { dg-options "-O2 -Wattributes -Wstringop-overflow -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __builtin_va_list va_list; + +#if __cplusplus +extern "C" { +#endif + +void* memchr (const void*, int, size_t); +int memcmp (const void*, const void*, size_t); +void* memcpy (void*, const void*, size_t); +void* memmove (void*, const void*, size_t); + +int printf (const char*, ...); +int puts (const char*); +int puts_unlocked (const char*); +int sprintf (char*, const char*, ...); +int snprintf (char*, size_t, const char*, ...); +int vsprintf (char*, const char*, va_list); +int vsnprintf (char*, size_t, const char*, va_list); + +int strcmp (const char*, const char*); +int strncmp (const char*, const char*, size_t); + +char* stpcpy (char*, const char*); +char* stpncpy (char*, const char*, size_t); + +char* strcat (char*, const char*); +char* strncat (char*, const char*, size_t); + +char* strcpy (char*, const char*); +char* strncpy (char*, const char*, size_t); + +char* strchr (const char*, int); +char* strdup (const char*); +size_t strlen (const char*); +size_t strnlen (const char*, size_t); +char* strndup (const char*, size_t); + +#if __cplusplus +} /* extern "C" */ +#endif + +#define NONSTRING __attribute__ ((nonstring)) + +char str[4]; +char arr[4] NONSTRING; + +char *ptr; +char *parr NONSTRING; + +struct MemArrays +{ + char str[4]; + char arr[4] NONSTRING; + char *parr NONSTRING; +}; + +void sink (int, ...); + + +#define T(call) sink (0, (call)) + +void test_printf (struct MemArrays *p) +{ + T (printf (str)); + T (printf (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (printf (ptr)); + T (printf (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (printf (p->str)); + T (printf (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_puts (struct MemArrays *p) +{ + T (puts (str)); + T (puts (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (puts (ptr)); + T (puts (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (puts (p->str)); + T (puts (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_snprintf (char *d, size_t n, struct MemArrays *p) +{ + T (snprintf (d, n, str)); + T (snprintf (d, n, arr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (snprintf (d, n, ptr)); + T (snprintf (d, n, parr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (snprintf (d, n, p->str)); + T (snprintf (d, n, p->arr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ +} + + +void test_sprintf (char *d, struct MemArrays *p) +{ + T (sprintf (d, str)); + T (sprintf (d, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (sprintf (d, ptr)); + T (sprintf (d, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (sprintf (d, p->str)); + T (sprintf (d, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + + +void test_vsnprintf (char *d, size_t n, struct MemArrays *p, va_list va) +{ + T (vsnprintf (d, n, str, va)); + T (vsnprintf (d, n, arr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (vsnprintf (d, n, ptr, va)); + T (vsnprintf (d, n, parr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (vsnprintf (d, n, p->str, va)); + T (vsnprintf (d, n, p->arr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ +} + + +void test_vsprintf (char *d, struct MemArrays *p, va_list va) +{ + T (vsprintf (d, str, va)); + T (vsprintf (d, arr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (vsprintf (d, ptr, va)); + T (vsprintf (d, parr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (vsprintf (d, p->str, va)); + T (vsprintf (d, p->arr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + + +void test_strcmp (struct MemArrays *p) +{ + T (strcmp (str, str)); + T (strcmp (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcmp (arr, str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strcmp (str, ptr)); + T (strcmp (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcmp (parr, str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strcmp (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcmp (p->arr, p->str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + T (strcmp (p->parr, p->str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_strncmp_warn (struct MemArrays *p) +{ + enum { N = sizeof arr }; + T (strncmp (str, arr, N)); + T (strncmp (arr, str, N)); + + T (strncmp (str, arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ + T (strncmp (arr, str, N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */ + + T (strncmp (str, parr, N + 1)); + T (strncmp (parr, str, N + 1)); + + T (strncmp (p->str, p->arr, N)); + T (strncmp (p->arr, p->str, N)); + T (strncmp (p->parr, p->str, N)); + + T (strncmp (p->str, p->arr, N)); + T (strncmp (p->arr, p->str, N)); + T (strncmp (p->parr, p->str, N)); +} + + +void test_strncmp_nowarn (struct MemArrays *p, size_t n) +{ + T (strncmp (str, str, n)); + T (strncmp (str, arr, n)); + T (strncmp (arr, str, n)); + + T (strncmp (str, ptr, n)); + T (strncmp (str, parr, n)); + T (strncmp (parr, str, n)); + + T (strncmp (p->str, p->arr, n)); + T (strncmp (p->arr, p->str, n)); + T (strncmp (p->parr, p->str, n)); +} + + +void test_stpcpy (struct MemArrays *p) +{ + T (stpcpy (str, str)); + T (stpcpy (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (stpcpy (arr, str)); + + T (stpcpy (str, ptr)); + T (stpcpy (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (stpcpy (parr, str)); + + T (stpcpy (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (stpcpy (p->arr, p->str)); + T (stpcpy (p->parr, p->str)); +} + + +void test_stpncpy_nowarn (struct MemArrays *p, unsigned n) +{ + T (stpncpy (str, str, n)); + T (stpncpy (str, arr, n)); + T (stpncpy (arr, str, n)); + + T (stpncpy (str, ptr, n)); + T (stpncpy (str, parr, n)); + T (stpncpy (parr, str, n)); + + T (stpncpy (p->str, p->arr, n)); + T (stpncpy (p->arr, p->str, n)); + T (stpncpy (p->parr, p->str, n)); +} + + +void test_stpncpy_warn (struct MemArrays *p, unsigned n) +{ + enum { N = sizeof arr }; + + T (stpncpy (str, str, N)); + T (stpncpy (str, arr, N)); + T (stpncpy (arr, str, N)); + + T (stpncpy (str, ptr, N)); + T (stpncpy (str, parr, N)); + T (stpncpy (parr, str, N)); + + T (stpncpy (p->str, p->arr, N)); + T (stpncpy (p->arr, p->str, N)); + T (stpncpy (p->parr, p->str, N)); + + T (stpncpy (ptr, str, N + 1)); + T (stpncpy (ptr, arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ + T (stpncpy (arr, str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ + + T (stpncpy (ptr, ptr, N + 1)); + T (stpncpy (ptr, parr, N + 1)); + T (stpncpy (parr, str, N + 1)); + + T (stpncpy (ptr, p->arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */ + T (stpncpy (p->arr, p->str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows" } */ + T (stpncpy (p->parr, p->str, N + 1)); +} + + +void test_strcat (struct MemArrays *p) +{ + T (strcat (str, str)); + T (strcat (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcat (arr, str)); + + T (strcat (str, ptr)); + T (strcat (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcat (parr, str)); + + T (strcat (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcat (p->arr, p->str)); + T (strcat (p->parr, p->str)); +} + + +void test_strncat (struct MemArrays *p, unsigned n) +{ + T (strncat (str, str, n)); + T (strncat (str, arr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strncat (arr, str, n)); + + T (strncat (str, ptr, n)); + T (strncat (str, parr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strncat (parr, str, n)); + + T (strncat (p->str, p->arr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strncat (p->arr, p->str, n)); + T (strncat (p->parr, p->str, n)); +} + + +void test_strcpy (struct MemArrays *p) +{ + T (strcpy (str, str)); + T (strcpy (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcpy (arr, str)); + + T (strcpy (str, ptr)); + T (strcpy (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcpy (parr, str)); + + T (strcpy (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcpy (p->arr, p->str)); + T (strcpy (p->parr, p->str)); +} + + +void test_strncpy (struct MemArrays *p, unsigned n) +{ + T (strncpy (str, str, n)); + T (strncpy (str, arr, n)); + T (strncpy (arr, str, n)); + + T (strncpy (str, ptr, n)); + T (strncpy (str, parr, n)); + T (strncpy (parr, str, n)); + + T (strncpy (p->str, p->arr, n)); + T (strncpy (p->arr, p->str, n)); + T (strncpy (p->parr, p->str, n)); +} + + +void test_strchr (struct MemArrays *p, int c) +{ + T (strchr (str, c)); + T (strchr (arr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strchr (ptr, c)); + T (strchr (parr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strchr (p->str, c)); + T (strchr (p->arr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_strdup (struct MemArrays *p) +{ + T (strdup (str)); + T (strdup (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strdup (ptr)); + T (strdup (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strdup (p->str)); + T (strdup (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_stnrdup_nowarn (struct MemArrays *p, size_t n) +{ + T (strndup (str, n)); + T (strndup (arr, n)); + + T (strndup (ptr, n)); + T (strndup (parr, n)); + + T (strndup (p->str, n)); + T (strndup (p->arr, n)); +} + + +void test_stnrdup_warn (struct MemArrays *p) +{ + enum { N = sizeof arr }; + + T (strndup (str, N)); + T (strndup (arr, N)); + + T (strndup (ptr, N)); + T (strndup (parr, N)); + + T (strndup (p->str, N)); + T (strndup (p->arr, N)); + + + T (strndup (arr, N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */ + T (strndup (parr, N + 1)); + T (strndup (p->arr, N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */ + T (strndup (p->parr, N + 1)); +} + + +void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n) +{ + T (strlen (str)); + T (strlen (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strlen (ptr)); + T (strlen (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strlen (p->str)); + T (strlen (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strlen (s)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + { + strcpy (s, "123"); + T (strlen (s)); + } + + { + char a[] __attribute__ ((nonstring)) = { 1, 2, 3 }; + + T (strlen (a)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + } + + { + char a[] __attribute__ ((nonstring)) = { 1, 2, 3, 4 }; + + strcpy (a, "12"); + T (strlen (a)); + } + + { + char *p __attribute__ ((nonstring)); + p = (char *)__builtin_malloc (n); + __builtin_memset (p, '*', n); + + T (strlen (p)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + strcpy (p, "12345"); + T (strlen (p)); + } +} + + +void test_strnlen (struct MemArrays *p, size_t n) +{ + T (strnlen (str, n)); + T (strnlen (arr, n)); + + T (strnlen (ptr, n)); + T (strnlen (parr, n)); + + T (strnlen (p->str, n)); + T (strnlen (p->arr, n)); +} + + +/* Verify no warnings are issued for raw mempory functions. */ + +void test_mem_functions (struct MemArrays *p, int c, size_t n) +{ + T (memchr (arr, c, n)); + T (memchr (parr, c, n)); + T (memchr (p->arr, c, n)); + T (memchr (p->parr, c, n)); + + T (memcmp (str, arr, n)); + T (memcmp (arr, str, n)); + T (memcmp (str, parr, n)); + T (memcmp (parr, str, n)); + T (memcmp (p->str, p->arr, n)); + T (memcmp (p->arr, p->str, n)); + T (memcmp (p->parr, p->str, n)); + + T (memcpy (str, arr, n)); + T (memcpy (arr, str, n)); + T (memcpy (str, parr, n)); + T (memcpy (parr, str, n)); + T (memcpy (p->str, p->arr, n)); + T (memcpy (p->arr, p->str, n)); + T (memcpy (p->parr, p->str, n)); + + T (memmove (str, arr, n)); + T (memmove (arr, str, n)); + T (memmove (str, parr, n)); + T (memmove (parr, str, n)); + T (memmove (p->str, p->arr, n)); + T (memmove (p->arr, p->str, n)); + T (memmove (p->parr, p->str, n)); +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index ff3230144b0..48b92417b57 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -51,6 +51,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "intl.h" #include "attribs.h" +#include "calls.h" /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value is an index into strinfo vector, negative value stands for @@ -152,8 +153,11 @@ struct decl_stridxlist_map mappings. */ static hash_map *decl_to_stridxlist_htab; +/* Hash table mapping strlen calls to stridx instances describing + the calls' arguments. Non-null only when warn_stringop_truncation + is non-zero. */ typedef std::pair stridx_strlenloc; -static hash_map strlen_to_stridx; +static hash_map *strlen_to_stridx; /* Obstack for struct stridxlist and struct decl_stridxlist_map. */ static struct obstack stridx_obstack; @@ -1207,8 +1211,11 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) gcc_assert (si->full_string_p); } - location_t loc = gimple_location (stmt); - strlen_to_stridx.put (lhs, stridx_strlenloc (idx, loc)); + if (strlen_to_stridx) + { + location_t loc = gimple_location (stmt); + strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); + } return; } } @@ -1253,8 +1260,11 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) set_strinfo (idx, si); find_equal_ptrs (src, idx); - location_t loc = gimple_location (stmt); - strlen_to_stridx.put (lhs, stridx_strlenloc (idx, loc)); + if (strlen_to_stridx) + { + location_t loc = gimple_location (stmt); + strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); + } } } @@ -1691,9 +1701,6 @@ is_strlen_related_p (tree src, tree len) static bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) { - if (!warn_stringop_truncation) - return false; - gimple *stmt = gsi_stmt (gsi); wide_int cntrange[2]; @@ -1733,35 +1740,15 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) return false; tree dst = gimple_call_arg (stmt, 0); - - /* See if the destination is declared with attribute "nonstring" - and if so, avoid the truncation warning. */ - if (TREE_CODE (dst) == SSA_NAME) - { - if (SSA_NAME_IS_DEFAULT_DEF (dst)) - dst = SSA_NAME_VAR (dst); - else - { - gimple *def = SSA_NAME_DEF_STMT (dst); - - if (is_gimple_assign (def) - && gimple_assign_rhs_code (def) == ADDR_EXPR) - dst = gimple_assign_rhs1 (def); - } - } - tree dstdecl = dst; if (TREE_CODE (dstdecl) == ADDR_EXPR) dstdecl = TREE_OPERAND (dstdecl, 0); - { - tree d = dstdecl; - if (TREE_CODE (d) == COMPONENT_REF) - d = TREE_OPERAND (d, 1); - - if (DECL_P (d) && lookup_attribute ("nonstring", DECL_ATTRIBUTES (d))) - return false; - } + /* If the destination refers to a an array/pointer declared nonstring + return early. */ + tree ref = NULL_TREE; + if (get_attr_nonstring_decl (dstdecl, &ref)) + return false; /* Look for dst[i] = '\0'; after the stxncpy() call and if found avoid the truncation warning. */ @@ -1770,12 +1757,32 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) if (!gsi_end_p (gsi) && is_gimple_assign (next_stmt)) { - HOST_WIDE_INT off; - dstdecl = get_addr_base_and_unit_offset (dstdecl, &off); - tree lhs = gimple_assign_lhs (next_stmt); - tree lhsbase = get_addr_base_and_unit_offset (lhs, &off); - if (lhsbase && operand_equal_p (dstdecl, lhsbase, 0)) + tree_code code = TREE_CODE (lhs); + if (code == ARRAY_REF || code == MEM_REF) + lhs = TREE_OPERAND (lhs, 0); + + tree func = gimple_call_fndecl (stmt); + if (DECL_FUNCTION_CODE (func) == BUILT_IN_STPNCPY) + { + tree ret = gimple_call_lhs (stmt); + if (ret && operand_equal_p (ret, lhs, 0)) + return false; + } + + /* Determine the base address and offset of the reference, + ignoring the innermost array index. */ + if (TREE_CODE (ref) == ARRAY_REF) + ref = TREE_OPERAND (ref, 0); + + HOST_WIDE_INT dstoff; + tree dstbase = get_addr_base_and_unit_offset (ref, &dstoff); + + HOST_WIDE_INT lhsoff; + tree lhsbase = get_addr_base_and_unit_offset (lhs, &lhsoff); + if (lhsbase + && dstoff == lhsoff + && operand_equal_p (dstbase, lhsbase, 0)) return false; } @@ -1909,6 +1916,9 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) { + if (!strlen_to_stridx) + return; + gimple *stmt = gsi_stmt (*gsi); bool with_bounds = gimple_call_with_bounds_p (stmt); @@ -1919,7 +1929,7 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) /* If the length argument was computed from strlen(S) for some string S retrieve the strinfo index for the string (PSS->FIRST) alonng with the location of the strlen() call (PSS->SECOND). */ - stridx_strlenloc *pss = strlen_to_stridx.get (len); + stridx_strlenloc *pss = strlen_to_stridx->get (len); if (!pss || pss->first <= 0) { if (maybe_diag_stxncpy_trunc (*gsi, src, len)) @@ -1953,13 +1963,13 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi) whether its value is known. Otherwise, issue the more generic -Wstringop-overflow which triggers for LEN arguments that in any meaningful way depend on strlen(SRC). */ - if (warn_stringop_truncation - && sisrc == silen - && is_strlen_related_p (src, len)) - warned = warning_at (callloc, OPT_Wstringop_truncation, - "%qD output truncated before terminating nul " - "copying as many bytes from a string as its length", - func); + if (sisrc == silen + && is_strlen_related_p (src, len) + && warning_at (callloc, OPT_Wstringop_truncation, + "%qD output truncated before terminating nul " + "copying as many bytes from a string as its length", + func)) + warned = true; else if (silen && is_strlen_related_p (src, silen->ptr)) warned = warning_at (callloc, OPT_Wstringop_overflow_, "%qD specified bound depends on the length " @@ -2966,9 +2976,12 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi) fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt), stmt); - tree rhs1 = gimple_assign_rhs1 (stmt); - if (stridx_strlenloc *ps = strlen_to_stridx.get (rhs1)) - strlen_to_stridx.put (lhs, stridx_strlenloc (*ps)); + if (strlen_to_stridx) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1)) + strlen_to_stridx->put (lhs, stridx_strlenloc (*ps)); + } } else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs)) { @@ -3199,6 +3212,10 @@ public: unsigned int pass_strlen::execute (function *fun) { + gcc_assert (!strlen_to_stridx); + if (warn_stringop_overflow || warn_stringop_truncation) + strlen_to_stridx = new hash_map (); + ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names); max_stridx = 1; @@ -3220,7 +3237,12 @@ pass_strlen::execute (function *fun) laststmt.len = NULL_TREE; laststmt.stridx = 0; - strlen_to_stridx.empty (); + if (strlen_to_stridx) + { + strlen_to_stridx->empty (); + delete strlen_to_stridx; + strlen_to_stridx = NULL; + } return 0; } -- 2.30.2