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
This commit is contained in:
parent
ab2c4ec8dc
commit
6a33d0ff21
@ -1,3 +1,21 @@
|
||||
2017-11-21 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
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 <Sergey.Shalnov@intel.com>
|
||||
|
||||
* config/i386/i386-opts.h (enum prefer_vector_width): Added new enum
|
||||
|
@ -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);
|
||||
|
208
gcc/calls.c
208
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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 <gcall *> (stmt);
|
||||
/* 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 <gcall *> (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);
|
||||
/* 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))
|
||||
if (!nonstring)
|
||||
{
|
||||
tree fndecl = gimple_call_fndecl (stmt);
|
||||
gcall *call = as_a <gcall *> (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 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 <gcall *> (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 <gcall *> (stmt);
|
||||
|
||||
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 <gcall *> (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);
|
||||
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. */
|
||||
|
@ -1,3 +1,11 @@
|
||||
2017-11-21 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
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 <Sergey.Shalnov@intel.com>
|
||||
|
||||
* g++.dg/ext/pr57362.C (__attribute__): Test
|
||||
|
105
gcc/testsuite/c-c++-common/Wstringop-truncation-2.c
Normal file
105
gcc/testsuite/c-c++-common/Wstringop-truncation-2.c
Normal file
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
474
gcc/testsuite/c-c++-common/attr-nonstring-3.c
Normal file
474
gcc/testsuite/c-c++-common/attr-nonstring-3.c
Normal file
@ -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));
|
||||
}
|
@ -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<tree_decl_hash, stridxlist> *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<int, location_t> stridx_strlenloc;
|
||||
static hash_map<tree, stridx_strlenloc> strlen_to_stridx;
|
||||
static hash_map<tree, stridx_strlenloc> *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<tree, stridx_strlenloc> ();
|
||||
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user