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