diff options
author | Martin Sebor <msebor@redhat.com> | 2017-11-21 20:01:58 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2017-11-21 13:01:58 -0700 |
commit | 6a33d0ff21e941fc3a65f23a753cc318aaae82b5 (patch) | |
tree | b3eedddc82aa715ade9b6b34cd5163e5fd23b551 /gcc/calls.c | |
parent | ab2c4ec8dcbe5d0b93d0250abd42ff9fb791e0b6 (diff) | |
download | gcc-6a33d0ff21e941fc3a65f23a753cc318aaae82b5.zip gcc-6a33d0ff21e941fc3a65f23a753cc318aaae82b5.tar.gz gcc-6a33d0ff21e941fc3a65f23a753cc318aaae82b5.tar.bz2 |
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
Diffstat (limited to 'gcc/calls.c')
-rw-r--r-- | gcc/calls.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/gcc/calls.c b/gcc/calls.c index 3730f43..cae543c 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 %<nonstring%> " + "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 %<nonstring%> " + "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 %<nonstring%>", + 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. |