PR c/81117 - Improve buffer overflow checking in strncpy
gcc/ChangeLog: PR c/81117 * builtins.c (compute_objsize): Handle arrays that compute_builtin_object_size likes to fail for. Make extern. * builtins.h (compute_objsize): Declare. (check_strncpy_sizes): New function. (expand_builtin_strncpy): Call check_strncpy_sizes. * gimple-fold.c (gimple_fold_builtin_strncpy): Implement -Wstringop-truncation. (gimple_fold_builtin_strncat): Same. * gimple.c (gimple_build_call_from_tree): Set call location. * tree-ssa-strlen.c (strlen_to_stridx): New global variable. (maybe_diag_bound_equal_length, is_strlen_related_p): New functions. (handle_builtin_stxncpy, handle_builtin_strncat): Same. (handle_builtin_strlen): Use strlen_to_stridx. (strlen_optimize_stmt): Handle flavors of strncat, strncpy, and stpncpy. Use strlen_to_stridx. (pass_strlen::execute): Release strlen_to_stridx. * doc/invoke.texi (-Wsizeof-pointer-memaccess): Document enhancement. (-Wstringop-truncation): Document new option. gcc/ada/ChangeLog: PR c/81117 * ada/adadecode.c (__gnat_decode): Use memcpy instead of strncpy. * ada/argv.c (__gnat_fill_arg, __gnat_fill_env): Same. gcc/c-family/ChangeLog: PR c/81117 * c-common.c (catenate_strings): Use memcpy instead of strncpy. * c-warn.c (sizeof_pointer_memaccess_warning): Handle arrays. * c.opt (-Wstringop-truncation): New option. gcc/fortran/ChangeLog: PR c/81117 * gcc/fortran/decl.c (build_sym): Use strcpy instead of strncpy. gcc/objc/ChangeLog: PR c/81117 * objc-encoding.c (encode_type): Use memcpy instead of strncpy. gcc/testsuite/ChangeLog: PR c/81117 * c-c++-common/Wsizeof-pointer-memaccess3.c: New test. * c-c++-common/Wstringop-overflow.c: Same. * c-c++-common/Wstringop-truncation.c: Same. * c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust. * c-c++-common/attr-nonstring-2.c: New test. * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Adjust. * g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same. * gcc.dg/torture/pr63554.c: Same. * gcc.dg/Walloca-1.c: Disable macro tracking. From-SVN: r254630
This commit is contained in:
parent
e89ce41dba
commit
025d57f037
|
@ -1,3 +1,26 @@
|
|||
2017-11-10 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/81117
|
||||
* builtins.c (compute_objsize): Handle arrays that
|
||||
compute_builtin_object_size likes to fail for. Make extern.
|
||||
* builtins.h (compute_objsize): Declare.
|
||||
(check_strncpy_sizes): New function.
|
||||
(expand_builtin_strncpy): Call check_strncpy_sizes.
|
||||
* gimple-fold.c (gimple_fold_builtin_strncpy): Implement
|
||||
-Wstringop-truncation.
|
||||
(gimple_fold_builtin_strncat): Same.
|
||||
* gimple.c (gimple_build_call_from_tree): Set call location.
|
||||
* tree-ssa-strlen.c (strlen_to_stridx): New global variable.
|
||||
(maybe_diag_bound_equal_length, is_strlen_related_p): New functions.
|
||||
(handle_builtin_stxncpy, handle_builtin_strncat): Same.
|
||||
(handle_builtin_strlen): Use strlen_to_stridx.
|
||||
(strlen_optimize_stmt): Handle flavors of strncat, strncpy, and
|
||||
stpncpy.
|
||||
Use strlen_to_stridx.
|
||||
(pass_strlen::execute): Release strlen_to_stridx.
|
||||
* doc/invoke.texi (-Wsizeof-pointer-memaccess): Document enhancement.
|
||||
(-Wstringop-truncation): Document new option.
|
||||
|
||||
2017-11-10 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR gcov-profile/82702
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2017-11-10 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/81117
|
||||
* ada/adadecode.c (__gnat_decode): Use memcpy instead of strncpy.
|
||||
* ada/argv.c (__gnat_fill_arg, __gnat_fill_env): Same.
|
||||
|
||||
2017-11-10 Eric Botcazou <ebotcazou@adacore.com>
|
||||
|
||||
* gcc-interface/utils.c (convert) <RECORD_TYPE>: Add comment and do
|
||||
|
|
|
@ -330,7 +330,7 @@ __gnat_decode (const char *coded_name, char *ada_name, int verbose)
|
|||
}
|
||||
|
||||
/* Write symbol in the space. */
|
||||
strncpy (optoken, trans_table[k][1], oplen);
|
||||
memcpy (optoken, trans_table[k][1], oplen);
|
||||
}
|
||||
else
|
||||
k++;
|
||||
|
|
|
@ -92,7 +92,7 @@ void
|
|||
__gnat_fill_arg (char *a, int i)
|
||||
{
|
||||
if (gnat_argv != NULL)
|
||||
strncpy (a, gnat_argv[i], strlen(gnat_argv[i]));
|
||||
memcpy (a, gnat_argv[i], strlen (gnat_argv[i]));
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -118,7 +118,7 @@ void
|
|||
__gnat_fill_env (char *a, int i)
|
||||
{
|
||||
if (gnat_envp != NULL)
|
||||
strncpy (a, gnat_envp[i], strlen (gnat_envp[i]));
|
||||
memcpy (a, gnat_envp[i], strlen (gnat_envp[i]));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -3260,18 +3260,60 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
|
|||
}
|
||||
|
||||
/* Helper to compute the size of the object referenced by the DEST
|
||||
expression which must of of pointer type, using Object Size type
|
||||
expression which must have pointer type, using Object Size type
|
||||
OSTYPE (only the least significant 2 bits are used). Return
|
||||
the size of the object if successful or NULL when the size cannot
|
||||
be determined. */
|
||||
|
||||
static inline tree
|
||||
tree
|
||||
compute_objsize (tree dest, int ostype)
|
||||
{
|
||||
unsigned HOST_WIDE_INT size;
|
||||
if (compute_builtin_object_size (dest, ostype & 3, &size))
|
||||
|
||||
/* Only the two least significant bits are meaningful. */
|
||||
ostype &= 3;
|
||||
|
||||
if (compute_builtin_object_size (dest, ostype, &size))
|
||||
return build_int_cst (sizetype, size);
|
||||
|
||||
/* Unless computing the largest size (for memcpy and other raw memory
|
||||
functions), try to determine the size of the object from its type. */
|
||||
if (!ostype)
|
||||
return NULL_TREE;
|
||||
|
||||
if (TREE_CODE (dest) == SSA_NAME)
|
||||
{
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (dest);
|
||||
if (!is_gimple_assign (stmt))
|
||||
return NULL_TREE;
|
||||
|
||||
tree_code code = gimple_assign_rhs_code (stmt);
|
||||
if (code != ADDR_EXPR && code != POINTER_PLUS_EXPR)
|
||||
return NULL_TREE;
|
||||
|
||||
dest = gimple_assign_rhs1 (stmt);
|
||||
}
|
||||
|
||||
if (TREE_CODE (dest) != ADDR_EXPR)
|
||||
return NULL_TREE;
|
||||
|
||||
tree type = TREE_TYPE (dest);
|
||||
if (TREE_CODE (type) == POINTER_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
|
||||
type = TYPE_MAIN_VARIANT (type);
|
||||
|
||||
if (TREE_CODE (type) == ARRAY_TYPE
|
||||
&& !array_at_struct_end_p (dest))
|
||||
{
|
||||
/* Return the constant size unless it's zero (that's a zero-length
|
||||
array likely at the end of a struct). */
|
||||
tree size = TYPE_SIZE_UNIT (type);
|
||||
if (size && TREE_CODE (size) == INTEGER_CST
|
||||
&& !integer_zerop (size))
|
||||
return size;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
|
@ -3923,6 +3965,22 @@ expand_builtin_strncat (tree exp, rtx)
|
|||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Helper to check the sizes of sequences and the destination of calls
|
||||
to __builtin_strncpy (DST, SRC, CNT) and __builtin___strncpy_chk.
|
||||
Returns true on success (no overflow warning), false otherwise. */
|
||||
|
||||
static bool
|
||||
check_strncpy_sizes (tree exp, tree dst, tree src, tree cnt)
|
||||
{
|
||||
tree dstsize = compute_objsize (dst, warn_stringop_overflow - 1);
|
||||
|
||||
if (!check_sizes (OPT_Wstringop_overflow_,
|
||||
exp, cnt, /*maxlen=*/NULL_TREE, src, dstsize))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Expand expression EXP, which is a call to the strncpy builtin. Return
|
||||
NULL_RTX if we failed the caller should emit a normal call. */
|
||||
|
||||
|
@ -3941,16 +3999,7 @@ expand_builtin_strncpy (tree exp, rtx target)
|
|||
/* The length of the source sequence. */
|
||||
tree slen = c_strlen (src, 1);
|
||||
|
||||
if (warn_stringop_overflow)
|
||||
{
|
||||
tree destsize = compute_objsize (dest,
|
||||
warn_stringop_overflow - 1);
|
||||
|
||||
/* The number of bytes to write is LEN but check_sizes will also
|
||||
check SLEN if LEN's value isn't known. */
|
||||
check_sizes (OPT_Wstringop_overflow_,
|
||||
exp, len, /*maxlen=*/NULL_TREE, src, destsize);
|
||||
}
|
||||
check_strncpy_sizes (exp, dest, src, len);
|
||||
|
||||
/* We must be passed a constant len and src parameter. */
|
||||
if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
|
||||
|
|
|
@ -89,6 +89,7 @@ extern tree fold_call_stmt (gcall *, bool);
|
|||
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
|
||||
extern bool is_simple_builtin (tree);
|
||||
extern bool is_inexpensive_builtin (tree);
|
||||
extern tree compute_objsize (tree, int);
|
||||
|
||||
extern bool readonly_data_expr (tree exp);
|
||||
extern bool init_target_chars (void);
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
2017-11-10 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/81117
|
||||
* c-common.c (catenate_strings): Use memcpy instead of strncpy.
|
||||
* c-warn.c (sizeof_pointer_memaccess_warning): Handle arrays.
|
||||
* c.opt (-Wstringop-truncation): New option.
|
||||
|
||||
2017-11-06 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR middle-end/82404
|
||||
|
|
|
@ -5890,10 +5890,10 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
|
|||
static char *
|
||||
catenate_strings (const char *lhs, const char *rhs_start, int rhs_size)
|
||||
{
|
||||
const int lhs_size = strlen (lhs);
|
||||
const size_t lhs_size = strlen (lhs);
|
||||
char *result = XNEWVEC (char, lhs_size + rhs_size);
|
||||
strncpy (result, lhs, lhs_size);
|
||||
strncpy (result + lhs_size, rhs_start, rhs_size);
|
||||
memcpy (result, lhs, lhs_size);
|
||||
memcpy (result + lhs_size, rhs_start, rhs_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -693,7 +693,8 @@ sizeof_pointer_memaccess_warning (location_t *sizeof_arg_loc, tree callee,
|
|||
|| vec_safe_length (params) <= 1)
|
||||
return;
|
||||
|
||||
switch (DECL_FUNCTION_CODE (callee))
|
||||
enum built_in_function fncode = DECL_FUNCTION_CODE (callee);
|
||||
switch (fncode)
|
||||
{
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_STRNCASECMP:
|
||||
|
@ -775,8 +776,27 @@ sizeof_pointer_memaccess_warning (location_t *sizeof_arg_loc, tree callee,
|
|||
|
||||
type = TYPE_P (sizeof_arg[idx])
|
||||
? sizeof_arg[idx] : TREE_TYPE (sizeof_arg[idx]);
|
||||
|
||||
if (!POINTER_TYPE_P (type))
|
||||
return;
|
||||
{
|
||||
/* The argument type may be an array. Diagnose bounded string
|
||||
copy functions that specify the bound in terms of the source
|
||||
argument rather than the destination. */
|
||||
if (strop && !cmp && fncode != BUILT_IN_STRNDUP && src)
|
||||
{
|
||||
tem = tree_strip_nop_conversions (src);
|
||||
if (TREE_CODE (tem) == ADDR_EXPR)
|
||||
tem = TREE_OPERAND (tem, 0);
|
||||
if (operand_equal_p (tem, sizeof_arg[idx], OEP_ADDRESS_OF))
|
||||
warning_at (sizeof_arg_loc[idx], OPT_Wsizeof_pointer_memaccess,
|
||||
"argument to %<sizeof%> in %qD call is the same "
|
||||
"expression as the source; did you mean to use "
|
||||
"the size of the destination?",
|
||||
callee);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dest
|
||||
&& (tem = tree_strip_nop_conversions (dest))
|
||||
|
|
|
@ -744,6 +744,10 @@ C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow) Ini
|
|||
Under the control of Object Size type, warn about buffer overflow in string
|
||||
manipulation functions like memcpy and strcpy.
|
||||
|
||||
Wstringop-truncation
|
||||
C ObjC C++ ObjC++ Var(warn_stringop_truncation) Warning Init (1) LangEnabledBy(C ObjC C++ ObjC++, Wall)
|
||||
Warn about truncation in string manipulation functions like strncat and strncpy.
|
||||
|
||||
Wsuggest-attribute=format
|
||||
C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
|
||||
Warn about functions which might be candidates for format attributes.
|
||||
|
|
|
@ -314,7 +314,7 @@ Objective-C and Objective-C++ Dialects}.
|
|||
-Wsizeof-pointer-memaccess -Wsizeof-array-argument @gol
|
||||
-Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
|
||||
-Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
|
||||
-Wstringop-overflow=@var{n} @gol
|
||||
-Wstringop-overflow=@var{n} -Wstringop-truncation @gol
|
||||
-Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}malloc@r{]} @gol
|
||||
-Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
|
||||
-Wmissing-format-attribute -Wsubobject-linkage @gol
|
||||
|
@ -5214,6 +5214,55 @@ whether to issue a warning. Similarly to @option{-Wstringop-overflow=3} this
|
|||
setting of the option may result in warnings for benign code.
|
||||
@end table
|
||||
|
||||
@item -Wstringop-truncation
|
||||
@opindex Wstringop-truncation
|
||||
@opindex Wno-stringop-truncation
|
||||
Warn for calls to bounded string manipulation functions such as @code{strncat},
|
||||
@code{strncpy}, and @code{stpncpy} that may either truncate the copied string
|
||||
or leave the destination unchanged.
|
||||
|
||||
In the following example, the call to @code{strncat} specifies a bound that
|
||||
is less than the length of the source string. As a result, the copy of
|
||||
the source will be truncated and so the call is diagnosed. To avoid the
|
||||
warning use @code{bufsize - strlen (buf) - 1)} as the bound.
|
||||
|
||||
@smallexample
|
||||
void append (char *buf, size_t bufsize)
|
||||
@{
|
||||
strncat (buf, ".txt", 3);
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
As another example, the following call to @code{strncpy} results in copying
|
||||
to @code{d} just the characters preceding the terminating NUL, without
|
||||
appending the NUL to the end. Assuming the result of @code{strncpy} is
|
||||
necessarily a NUL-terminated string is a common mistake, and so the call
|
||||
is diagnosed. To avoid the warning when the result is not expected to be
|
||||
NUL-terminated, call @code{memcpy} instead.
|
||||
|
||||
@smallexample
|
||||
void copy (char *d, const char *s)
|
||||
@{
|
||||
strncpy (d, s, strlen (s));
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
In the following example, the call to @code{strncpy} specifies the size
|
||||
of the destination buffer as the bound. If the length of the source
|
||||
string is equal to or greater than this size the result of the copy will
|
||||
not be NUL-terminated. Therefore, the call is also diagnosed. To avoid
|
||||
the warning, specify @code{sizeof buf - 1} as the bound and set the last
|
||||
element of the buffer to @code{NUL}.
|
||||
|
||||
@smallexample
|
||||
void copy (const char *s)
|
||||
@{
|
||||
char buf[80];
|
||||
strncpy (buf, s, sizeof buf);
|
||||
@dots{}
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
|
||||
@opindex Wsuggest-attribute=
|
||||
@opindex Wno-suggest-attribute=
|
||||
|
@ -6230,11 +6279,26 @@ not an array, but a pointer. This warning is enabled by @option{-Wall}.
|
|||
@opindex Wsizeof-pointer-memaccess
|
||||
@opindex Wno-sizeof-pointer-memaccess
|
||||
Warn for suspicious length parameters to certain string and memory built-in
|
||||
functions if the argument uses @code{sizeof}. This warning warns e.g.@:
|
||||
about @code{memset (ptr, 0, sizeof (ptr));} if @code{ptr} is not an array,
|
||||
but a pointer, and suggests a possible fix, or about
|
||||
@code{memcpy (&foo, ptr, sizeof (&foo));}. This warning is enabled by
|
||||
@option{-Wall}.
|
||||
functions if the argument uses @code{sizeof}. This warning triggers for
|
||||
example for @code{memset (ptr, 0, sizeof (ptr));} if @code{ptr} is not
|
||||
an array, but a pointer, and suggests a possible fix, or about
|
||||
@code{memcpy (&foo, ptr, sizeof (&foo));}. @option{-Wsizeof-pointer-memaccess}
|
||||
also warns about calls to bounded string copy functions like @code{strncat}
|
||||
or @code{strncpy} that specify as the bound a @code{sizeof} expression of
|
||||
the source array. For example, in the following function the call to
|
||||
@code{strncat} specifies the size of the source string as the bound. That
|
||||
is almost certainly a mistake and so the call is diagnosed.
|
||||
@smallexample
|
||||
void make_file (const char *name)
|
||||
@{
|
||||
char path[PATH_MAX];
|
||||
strncpy (path, name, sizeof path - 1);
|
||||
strncat (path, ".text", sizeof ".text");
|
||||
@dots{}
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
The @option{-Wsizeof-pointer-memaccess} option is enabled by @option{-Wall}.
|
||||
|
||||
@item -Wsizeof-array-argument
|
||||
@opindex Wsizeof-array-argument
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2017-11-10 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/81117
|
||||
* gcc/fortran/decl.c (build_sym): Use strcpy instead of strncpy.
|
||||
|
||||
2017-11-10 Paul Thomas <pault@gcc.gnu.org>
|
||||
|
||||
PR fortran/82934
|
||||
|
|
|
@ -1427,11 +1427,9 @@ build_sym (const char *name, gfc_charlen *cl, bool cl_deferred,
|
|||
{
|
||||
char u_name[GFC_MAX_SYMBOL_LEN + 1];
|
||||
gfc_symtree *st;
|
||||
int nlen;
|
||||
|
||||
nlen = strlen(name);
|
||||
gcc_assert (nlen <= GFC_MAX_SYMBOL_LEN);
|
||||
strncpy (u_name, name, nlen + 1);
|
||||
gcc_assert (strlen(name) <= GFC_MAX_SYMBOL_LEN);
|
||||
strcpy (u_name, name);
|
||||
u_name[0] = upper;
|
||||
|
||||
st = gfc_find_symtree (gfc_current_ns->sym_root, u_name);
|
||||
|
|
|
@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "gimple-iterator.h"
|
||||
#include "tree-into-ssa.h"
|
||||
#include "tree-dfa.h"
|
||||
#include "tree-object-size.h"
|
||||
#include "tree-ssa.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "ipa-utils.h"
|
||||
|
@ -59,6 +60,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "asan.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "intl.h"
|
||||
|
||||
/* Return true when DECL can be referenced from current unit.
|
||||
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
|
||||
|
@ -1553,12 +1556,28 @@ static bool
|
|||
gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
|
||||
tree dest, tree src, tree len)
|
||||
{
|
||||
location_t loc = gimple_location (gsi_stmt (*gsi));
|
||||
tree fn;
|
||||
gimple *stmt = gsi_stmt (*gsi);
|
||||
location_t loc = gimple_location (stmt);
|
||||
|
||||
/* If the LEN parameter is zero, return DEST. */
|
||||
if (integer_zerop (len))
|
||||
{
|
||||
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);
|
||||
|
||||
replace_call_with_value (gsi, dest);
|
||||
return true;
|
||||
}
|
||||
|
@ -1573,16 +1592,66 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
|
|||
if (!slen || TREE_CODE (slen) != INTEGER_CST)
|
||||
return false;
|
||||
|
||||
slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
|
||||
/* The size of the source string including the terminating nul. */
|
||||
tree ssize = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
|
||||
|
||||
/* We do not support simplification of this case, though we do
|
||||
support it when expanding trees into RTL. */
|
||||
/* FIXME: generate a call to __builtin_memset. */
|
||||
if (tree_int_cst_lt (slen, len))
|
||||
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 <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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* OK transform into builtin memcpy. */
|
||||
fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
|
||||
tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
|
||||
if (!fn)
|
||||
return false;
|
||||
|
||||
|
@ -1591,6 +1660,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
|
|||
NULL_TREE, true, GSI_SAME_STMT);
|
||||
gimple *repl = gimple_build_call (fn, 3, dest, src, len);
|
||||
replace_call_with_call_and_fold (gsi, repl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1887,24 +1957,71 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* If the requested len is greater than or equal to the string
|
||||
length, call strcat. */
|
||||
if (TREE_CODE (len) == INTEGER_CST && p
|
||||
&& compare_tree_int (len, strlen (p)) >= 0)
|
||||
if (TREE_CODE (len) != INTEGER_CST || !p)
|
||||
return false;
|
||||
|
||||
unsigned srclen = strlen (p);
|
||||
|
||||
int cmpsrc = compare_tree_int (len, srclen);
|
||||
|
||||
/* Return early if the requested len is less than the string length.
|
||||
Warnings will be issued elsewhere later. */
|
||||
if (cmpsrc < 0)
|
||||
return false;
|
||||
|
||||
unsigned HOST_WIDE_INT dstsize;
|
||||
|
||||
bool nowarn = gimple_no_warning_p (stmt);
|
||||
|
||||
if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
|
||||
{
|
||||
tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
|
||||
int cmpdst = compare_tree_int (len, dstsize);
|
||||
|
||||
/* If the replacement _DECL isn't initialized, don't do the
|
||||
transformation. */
|
||||
if (!fn)
|
||||
return false;
|
||||
if (cmpdst >= 0)
|
||||
{
|
||||
tree fndecl = gimple_call_fndecl (stmt);
|
||||
|
||||
gcall *repl = gimple_build_call (fn, 2, dst, src);
|
||||
replace_call_with_call_and_fold (gsi, repl);
|
||||
return true;
|
||||
/* Strncat copies (at most) LEN bytes and always appends
|
||||
the terminating NUL so the specified bound should never
|
||||
be equal to (or greater than) the size of the destination.
|
||||
If it is, the copy could overflow. */
|
||||
location_t loc = gimple_location (stmt);
|
||||
nowarn = warning_at (loc, OPT_Wstringop_overflow_,
|
||||
cmpdst == 0
|
||||
? G_("%G%qD specified bound %E equals "
|
||||
"destination size")
|
||||
: G_("%G%qD specified bound %E exceeds "
|
||||
"destination size %wu"),
|
||||
stmt, fndecl, len, dstsize);
|
||||
if (nowarn)
|
||||
gimple_set_no_warning (stmt, true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!nowarn && cmpsrc == 0)
|
||||
{
|
||||
tree fndecl = gimple_call_fndecl (stmt);
|
||||
|
||||
/* To avoid certain truncation the specified bound should also
|
||||
not be equal to (or less than) the length of the source. */
|
||||
location_t loc = gimple_location (stmt);
|
||||
if (warning_at (loc, OPT_Wstringop_overflow_,
|
||||
"%G%qD specified bound %E equals source length",
|
||||
stmt, fndecl, len))
|
||||
gimple_set_no_warning (stmt, true);
|
||||
}
|
||||
|
||||
tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
|
||||
|
||||
/* If the replacement _DECL isn't initialized, don't do the
|
||||
transformation. */
|
||||
if (!fn)
|
||||
return false;
|
||||
|
||||
/* Otherwise, emit a call to strcat. */
|
||||
gcall *repl = gimple_build_call (fn, 2, dst, src);
|
||||
replace_call_with_call_and_fold (gsi, repl);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC,
|
||||
|
|
|
@ -361,6 +361,7 @@ gimple_build_call_from_tree (tree t, tree fnptrtype)
|
|||
gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
|
||||
|
||||
gimple_set_block (call, TREE_BLOCK (t));
|
||||
gimple_set_location (call, EXPR_LOCATION (t));
|
||||
|
||||
/* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */
|
||||
gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2017-11-10 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/81117
|
||||
* objc-encoding.c (encode_type): Use memcpy instead of strncpy.
|
||||
|
||||
2017-10-31 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* objc-gnu-runtime-abi-01.c (objc_gnu_runtime_abi_01_init): Use
|
||||
|
|
|
@ -734,7 +734,7 @@ encode_type (tree type, int curtype, int format)
|
|||
|
||||
/* Rewrite "in const" from "nr" to "rn". */
|
||||
if (curtype >= 1 && !strncmp (enc - 1, "nr", 2))
|
||||
strncpy (enc - 1, "rn", 2);
|
||||
memcpy (enc - 1, "rn", 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
2017-11-10 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/81117
|
||||
* c-c++-common/Wsizeof-pointer-memaccess3.c: New test.
|
||||
* c-c++-common/Wstringop-overflow.c: Same.
|
||||
* c-c++-common/Wstringop-truncation.c: Same.
|
||||
* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust.
|
||||
* c-c++-common/attr-nonstring-2.c: New test.
|
||||
* gcc/testsuite/gcc.dg/builtin-stpncpy.c: Adjust.
|
||||
* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
|
||||
* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
|
||||
* gcc.dg/torture/pr63554.c: Same.
|
||||
* gcc.dg/Walloca-1.c: Disable macro tracking.
|
||||
|
||||
2017-11-10 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR tree-optimization/82929
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* Test -Wsizeof-pointer-memaccess warnings. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -ftrack-macro-expansion=0" } */
|
||||
/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
|
||||
/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
|
||||
/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
|
||||
/* { dg-require-effective-target alloca } */
|
||||
|
||||
#define bos(ptr) __builtin_object_size (ptr, 1)
|
||||
|
@ -473,12 +473,15 @@ f4 (char *x, char **y, int z, char w[64])
|
|||
strncat (w, s2, sizeof (w)); /* { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" } */
|
||||
stpncpy (w, s1, sizeof (w)); /* { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" } */
|
||||
|
||||
/* These are correct, no warning. */
|
||||
/* These are pointless when the destination is large enough, and
|
||||
cause overflow otherwise. If the copies are guaranteed to be
|
||||
safe the calls might as well be replaced by strcat(), strcpy(),
|
||||
or memcpy(). */
|
||||
const char s3[] = "foobarbaz";
|
||||
const char s4[] = "abcde12345678";
|
||||
strncpy (x, s3, sizeof (s3));
|
||||
strncat (x, s4, sizeof (s4));
|
||||
stpncpy (x, s3, sizeof (s3));
|
||||
strncpy (x, s3, sizeof (s3)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
|
||||
strncat (x, s4, sizeof (s4)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
|
||||
stpncpy (x, s3, sizeof (s3)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
|
||||
}
|
||||
|
||||
/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/* Test -Wsizeof-pointer-memaccess warnings. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wsizeof-pointer-memaccess -Wno-stringop-overflow -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
|
||||
|
||||
#define bos(ptr) __builtin_object_size (ptr, 1)
|
||||
#define bos0(ptr) __builtin_object_size (ptr, 0)
|
||||
|
||||
#define memset(dst, val, sz) \
|
||||
(FUNC (memset, dst, val, sz, bos (dst)), sink ((dst)))
|
||||
|
||||
#define memcpy(dst, src, sz) \
|
||||
(FUNC (memcpy, dst, src, sz, bos (dst)), sink ((dst)))
|
||||
|
||||
#define memmove(dst, src, sz) \
|
||||
(FUNC (memmove, dst, src, sz, bos (dst)), sink ((dst)))
|
||||
|
||||
#define mempcpy(dst, src, sz) \
|
||||
(FUNC (mempcpy, dst, src, sz, bos (dst)), sink ((dst)))
|
||||
|
||||
#define strncpy(dst, src, sz) \
|
||||
(FUNC (strncpy, dst, src, sz, bos (dst)), sink (dst))
|
||||
|
||||
#define strncat(dst, src, sz) \
|
||||
(FUNC (strncat, dst, src, sz, bos (dst)), sink (dst))
|
||||
|
||||
#define stpncpy(dst, src, sz) \
|
||||
(FUNC (stpncpy, dst, src, sz, bos (dst)), sink (dst))
|
||||
|
||||
void sink (void*);
|
||||
|
||||
#define S10 "123456789"
|
||||
extern char a10[10];
|
||||
|
||||
void test_string_literal (char *dst)
|
||||
{
|
||||
#define FUNC(f, d, s, n, x) __builtin_ ## f (d, s, n)
|
||||
|
||||
/* It's common to call memcpy and other raw memory functions with
|
||||
size drerived from the source argument. Verify that no warning
|
||||
is ussued for such calls. */
|
||||
memcpy (dst, S10, sizeof S10);
|
||||
mempcpy (dst, S10, sizeof S10);
|
||||
memmove (dst, S10, sizeof S10);
|
||||
|
||||
memset (dst, 0, sizeof S10);
|
||||
|
||||
stpncpy (dst, S10, sizeof S10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncpy (dst, S10, sizeof S10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncat (dst, S10, sizeof S10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
/* Unlike in the cases above, even though the calls below are likely
|
||||
wrong, it's not easy to detect that the expression (sizeof X - 1)
|
||||
involves sizeof of the source, so no warning is issued here, as
|
||||
helpful as one might be. Whether -Wstringop-truncation is issued
|
||||
is tested elsewhere. */
|
||||
stpncpy (dst, S10, sizeof S10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncpy (dst, S10, sizeof S10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncat (dst, S10, sizeof S10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
}
|
||||
|
||||
|
||||
void test_char_array (char *dst)
|
||||
{
|
||||
memcpy (dst, a10, sizeof a10);
|
||||
mempcpy (dst, a10, sizeof a10);
|
||||
memmove (dst, a10, sizeof a10);
|
||||
|
||||
memset (dst, 0, sizeof a10);
|
||||
|
||||
stpncpy (dst, a10, sizeof a10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncpy (dst, a10, sizeof a10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncat (dst, a10, sizeof a10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
stpncpy (dst, a10, sizeof a10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncpy (dst, a10, sizeof a10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncat (dst, a10, sizeof a10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
}
|
||||
|
||||
|
||||
#undef FUNC
|
||||
#define FUNC(f, d, s, n, os) __builtin___ ## f ## _chk (d, s, n, os)
|
||||
|
||||
void test_char_array_chk (char *dst)
|
||||
{
|
||||
memcpy (dst, S10, sizeof S10);
|
||||
mempcpy (dst, S10, sizeof S10);
|
||||
memmove (dst, S10, sizeof S10);
|
||||
|
||||
memset (dst, 0, sizeof S10);
|
||||
|
||||
stpncpy (dst, S10, sizeof S10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncpy (dst, S10, sizeof S10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncat (dst, S10, sizeof S10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
stpncpy (dst, S10, sizeof S10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncpy (dst, S10, sizeof S10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncat (dst, S10, sizeof S10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
}
|
||||
|
||||
|
||||
void test_string_literal_chk (char *dst)
|
||||
{
|
||||
memcpy (dst, a10, sizeof a10);
|
||||
mempcpy (dst, a10, sizeof a10);
|
||||
memmove (dst, a10, sizeof a10);
|
||||
|
||||
memset (dst, 0, sizeof a10);
|
||||
|
||||
stpncpy (dst, a10, sizeof a10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncpy (dst, a10, sizeof a10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
strncat (dst, a10, sizeof a10); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" } */
|
||||
|
||||
stpncpy (dst, a10, sizeof a10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncpy (dst, a10, sizeof a10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
|
||||
strncat (dst, a10, sizeof a10 - 1); /* { dg-warning "\\\[-Wsizeof-pointer-memaccess]" "" { xfail *-*-* } } */
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/* PR middle-end/81117 - Improve buffer overflow checking in strncpy
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wstringop-overflow -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t strlen (const char*);
|
||||
char* strncat (char*, const char*, size_t);
|
||||
char* strncpy (char*, const char*, size_t);
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
const char ar[] = "123";
|
||||
|
||||
void test_strncat (char **d, const char* s, int i)
|
||||
{
|
||||
/* Use a fresh pointer for each test to prevent the optimizer from
|
||||
eliminating redundant writes into the same destination. Avoid
|
||||
calling functions like sink() on the result that would have to
|
||||
be assumed to change the source string by the alias oracle. */
|
||||
#define T(d, s, len) strncat (*d++, (s), (len))
|
||||
|
||||
T (d, "", 0);
|
||||
T (d, "", 1);
|
||||
T (d, "", 2);
|
||||
T (d, "", 3);
|
||||
T (d, "123", 0);
|
||||
/* The following two calls truncate the copy and are diagnosed
|
||||
by -Wstringop-truncation but there is evidence of overflow so
|
||||
they're not diagnosed by -Wstringop-overflow. */
|
||||
T (d, "123", 1);
|
||||
T (d, "123", 2);
|
||||
|
||||
T (d, "123", 3); /* { dg-warning ".strncat\[^\n\r\]* specified bound 3 equals source length" } */
|
||||
T (d, "123", 4);
|
||||
T (d, "123", 9);
|
||||
|
||||
T (d, s, strlen (s)); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
T (d, s, strlen (s) + 1); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
/* The following could also be diagnosed by -Wstringop-truncation
|
||||
(with some effort to distinguish the pattern from others like
|
||||
the one above. */
|
||||
T (d, s, strlen (s) - 1); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
T (d, s, strlen (s) - i); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
|
||||
/* The following is dubious but not necessarily a smoking gun. */
|
||||
T (d, s, strlen (s) - strlen (s));
|
||||
|
||||
{
|
||||
signed char n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
|
||||
{
|
||||
short n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
|
||||
{
|
||||
int n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
|
||||
{
|
||||
unsigned n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
|
||||
{
|
||||
size_t n;
|
||||
n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
|
||||
{
|
||||
size_t n;
|
||||
n = strlen (s) - 1; /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-message ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
|
||||
{
|
||||
/* This doesn't overflow so iit should not be diagnosed. */
|
||||
size_t n = strlen (s) - strlen (s);
|
||||
T (d, s, n);
|
||||
}
|
||||
|
||||
{
|
||||
size_t n = i < strlen (s) ? i : strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-message ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_strncpy (char **d, const char* s, int i)
|
||||
{
|
||||
#undef T
|
||||
#define T(d, s, len) strncpy (*d++, (s), (len))
|
||||
|
||||
T (d, "", 0);
|
||||
T (d, "", 1);
|
||||
T (d, "", 2);
|
||||
T (d, "", 3);
|
||||
T (d, "123", 0);
|
||||
T (d, "123", 1);
|
||||
T (d, "123", 2);
|
||||
T (d, "123", 3);
|
||||
T (d, "123", 4);
|
||||
T (d, "123", 9);
|
||||
|
||||
T (d, "123", sizeof "123");
|
||||
T (d, ar, sizeof ar);
|
||||
|
||||
T (d, s, strlen (s)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
|
||||
{
|
||||
int n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
}
|
||||
|
||||
{
|
||||
unsigned n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
}
|
||||
|
||||
{
|
||||
size_t n;
|
||||
n = strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
}
|
||||
|
||||
{
|
||||
size_t n;
|
||||
n = strlen (s) - 1; /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
}
|
||||
|
||||
{
|
||||
/* This is diagnosed by -Wstringop-truncation. Verify that it isn't
|
||||
also diagnosed by -Wstringop-overflow. */
|
||||
size_t n = strlen (s) - strlen (s);
|
||||
T (d, s, n);
|
||||
}
|
||||
|
||||
{
|
||||
/* This use of strncpy is certainly dubious and it could well be
|
||||
diagnosed by -Wstringop-truncation but it isn't. That it is
|
||||
diagnosed with -Wstringop-overflow is more by accident than
|
||||
by design. -Wstringop-overflow considers any dependency of
|
||||
the bound on strlen(s) a potential bug. */
|
||||
size_t n = i < strlen (s) ? i : strlen (s); /* { dg-message "length computed here" } */
|
||||
T (d, s, n); /* { dg-message ".strncpy\[^\n\r]* specified bound depends on the length of the source argument" } */
|
||||
}
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
/* PR middle-end/81117 - Improve buffer overflow checking in strncpy
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wstringop-truncation -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
|
||||
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t strlen (const char*);
|
||||
char* strncat (char*, const char*, size_t);
|
||||
char* strncpy (char*, const char*, size_t);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
extern size_t unsigned_value (void)
|
||||
{
|
||||
extern volatile size_t unsigned_value_source;
|
||||
return unsigned_value_source;
|
||||
}
|
||||
|
||||
size_t unsigned_range (size_t min, size_t max)
|
||||
{
|
||||
size_t val = unsigned_value ();
|
||||
return val < min || max < val ? min : val;
|
||||
}
|
||||
|
||||
#define UR(min, max) unsigned_range (min, max)
|
||||
|
||||
void sink (void*);
|
||||
|
||||
#define S4 "123"
|
||||
const char a4[] = "123";
|
||||
|
||||
#define CHOOSE(a, b) (unsigned_value () & 1 ? a : b)
|
||||
|
||||
|
||||
typedef struct Dest
|
||||
{
|
||||
char a5[5];
|
||||
char b7[7];
|
||||
char c3ns[3] __attribute__ ((nonstring));
|
||||
} Dest;
|
||||
|
||||
char dst7[7];
|
||||
char dst2_5[2][5];
|
||||
|
||||
/* Verify strncat warnings for arrays of known bounds. */
|
||||
|
||||
void test_strncat_array (Dest *pd)
|
||||
{
|
||||
#define CAT(d, s, len) (strncat ((d), (s), (len)), sink (d))
|
||||
|
||||
CAT (dst7, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
|
||||
|
||||
CAT (dst7, a4, 1); /* { dg-warning "output truncated copying 1 byte from a string of length 3" } */
|
||||
|
||||
/* There is no truncation here but possible overflow so these
|
||||
are diagnosed by -Wstringop-overflow:
|
||||
CAT (dst7, S4, 3);
|
||||
CAT (dst7, a4, 3);
|
||||
*/
|
||||
|
||||
CAT (pd->a5, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
|
||||
CAT (pd->a5, S4, 1); /* { dg-warning "output truncated copying 1 byte from a string of length 3" } */
|
||||
}
|
||||
|
||||
/* Verify strncat warnings for arrays of known bounds and a non-const
|
||||
character count in some range. */
|
||||
|
||||
void test_strncat_array_range (Dest *pd)
|
||||
{
|
||||
CAT (dst7, S4, UR (0, 1)); /* { dg-warning "output truncated copying between 0 and 1 bytes from a string of length 3" } */
|
||||
CAT (dst7, S4, UR (0, 2)); /* { dg-warning "output truncated copying between 0 and 2 bytes from a string of length 3" } */
|
||||
CAT (dst7, S4, UR (1, 3)); /* { dg-warning "output truncated copying between 1 and 3 bytes from a string of length 3" } */
|
||||
CAT (dst7, S4, UR (2, 4)); /* { dg-warning "output may be truncated copying between 2 and 4 bytes from a string of length 3" } */
|
||||
|
||||
CAT (dst7, S4, UR (0, 7));
|
||||
CAT (dst7, S4, UR (1, 7));
|
||||
CAT (dst7, S4, UR (6, 7));
|
||||
|
||||
CAT (dst7, S4, UR (0, 99));
|
||||
|
||||
CAT (dst7, S4, UR (0, 99));
|
||||
}
|
||||
|
||||
/* Verify strncat warnings for arrays of unknown bounds. */
|
||||
|
||||
void test_strncat_vla (char *d, unsigned n)
|
||||
{
|
||||
CAT (d, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
|
||||
CAT (d, S4, 4);
|
||||
|
||||
CAT (d, a4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
|
||||
|
||||
/* There is no truncation here but possible overflow so these
|
||||
are diagnosed by -Wstringop-overflow:
|
||||
CAT (d, S4, 3);
|
||||
CAT (d, a4, 3);
|
||||
*/
|
||||
CAT (d, a4, 4);
|
||||
|
||||
char vla[n];
|
||||
|
||||
CAT (vla, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
|
||||
|
||||
CAT (vla, S4, 4);
|
||||
CAT (vla, S4, n);
|
||||
|
||||
CAT (vla, a4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
|
||||
|
||||
CAT (vla, a4, 4);
|
||||
CAT (vla, a4, n);
|
||||
|
||||
CAT (d, vla, 1);
|
||||
CAT (d, vla, 3);
|
||||
CAT (d, vla, 4);
|
||||
CAT (d, vla, n);
|
||||
|
||||
/* There is no truncation here but possible overflow so these
|
||||
are diagnosed by -Wstringop-overflow:
|
||||
CAT (vla, S4, 3);
|
||||
CAT (vla, a4, 3);
|
||||
*/
|
||||
}
|
||||
|
||||
/* Verify strncpy warnings with at least one pointer to an object
|
||||
or string of unknown size (destination) or length (source). */
|
||||
|
||||
void test_strncpy_ptr (char *d, const char* s, const char *t, int i)
|
||||
{
|
||||
#define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
|
||||
|
||||
/* Strncpy doesn't nul-terminate so the following is diagnosed. */
|
||||
CPY (d, "", 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
CPY (d, s, 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
|
||||
/* This is safe. */
|
||||
CPY (d, "", 1);
|
||||
CPY (d, "", 2);
|
||||
|
||||
/* This could be safe. */
|
||||
CPY (d, s, 1);
|
||||
CPY (d, s, 2);
|
||||
|
||||
/* Truncation. */
|
||||
CPY (d, "123", 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 3" } */
|
||||
CPY (d, "123", 2); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 2 bytes from a string of length 3" } */
|
||||
CPY (d, "123", 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
CPY (d, "123", 4);
|
||||
CPY (d, "123", 9);
|
||||
|
||||
CPY (d, S4, sizeof S4); /* Covered by -Wsizeof-pointer-memaccess. */
|
||||
CPY (d, S4, sizeof S4 - 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
|
||||
CPY (d, a4, sizeof a4); /* Covered by -Wsizeof-pointer-memaccess. */
|
||||
CPY (d, a4, sizeof a4 - 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
CPY (d, a4, sizeof a4 - 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 3" } */
|
||||
CPY (d, a4, sizeof a4 - 4); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes from a string of length 3" } */
|
||||
|
||||
CPY (d, S4, strlen (S4)); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
/* Likely buggy but no truncation. Diagnosed by -Wstringop-overflow. */
|
||||
CPY (d, a4, strlen (a4) + 1);
|
||||
CPY (d, S4, strlen (S4) + i);
|
||||
|
||||
CPY (d, a4, strlen (a4)); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
/* As above, buggy but no evidence of truncation. */
|
||||
CPY (d, S4, strlen (S4) + 1);
|
||||
|
||||
CPY (d, CHOOSE ("", "1"), 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
CPY (d, CHOOSE ("1", "12"), 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
|
||||
CPY (d, CHOOSE ("", "1"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 1" } */
|
||||
CPY (d, CHOOSE ("1", ""), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 1" } */
|
||||
CPY (d, CHOOSE (s, "1"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 1" } */
|
||||
CPY (d, CHOOSE (s, t), 1);
|
||||
|
||||
CPY (d, CHOOSE ("", "1"), 2);
|
||||
CPY (d, CHOOSE ("1", ""), 2);
|
||||
CPY (d, CHOOSE ("1", "2"), 2);
|
||||
CPY (d, CHOOSE ("1", s), 2);
|
||||
CPY (d, CHOOSE (s, "1"), 2);
|
||||
CPY (d, CHOOSE (s, t), 2);
|
||||
|
||||
CPY (d, CHOOSE ("", "123"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 3" } */
|
||||
CPY (d, CHOOSE ("1", "123"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 1" } */
|
||||
CPY (d, CHOOSE ("12", "123"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 2" } */
|
||||
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" } */
|
||||
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" } */
|
||||
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" } */
|
||||
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" } */
|
||||
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" } */
|
||||
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" } */
|
||||
CPY (dp2, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
|
||||
}
|
||||
|
||||
{
|
||||
/* The following is likely buggy but there's no apparent truncation
|
||||
so it's not diagnosed by -Wstringop-truncation. Instead, it is
|
||||
diagnosed by -Wstringop-overflow (tested elsewhere). */
|
||||
int n;
|
||||
n = strlen (s) - 1;
|
||||
CPY (d, s, n);
|
||||
}
|
||||
|
||||
{
|
||||
/* Same as above. */
|
||||
size_t n;
|
||||
n = strlen (s) - 1;
|
||||
CPY (d, s, n);
|
||||
}
|
||||
|
||||
{
|
||||
size_t n = strlen (s) - strlen (s);
|
||||
CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
}
|
||||
|
||||
{
|
||||
/* This use of strncpy is dubious but it's probably not worth
|
||||
worrying about (truncation may not actually take place when
|
||||
i is the result). It is diagnosed with -Wstringop-overflow
|
||||
(although more by accident than by design).
|
||||
|
||||
size_t n = i < strlen (s) ? i : strlen (s);
|
||||
CPY (d, s, n);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Verify strncpy warnings for arrays of known bounds. */
|
||||
|
||||
void test_strncpy_array (Dest *pd, int i, const char* s)
|
||||
{
|
||||
#undef CPY
|
||||
#define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
|
||||
|
||||
CPY (dst7, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */
|
||||
CPY (dst7, s, sizeof dst7); /* { dg-warning "specified bound 7 equals destination size" } */
|
||||
|
||||
CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" { xfail *-*-* } } */
|
||||
CPY (dst2_5[1], s, sizeof dst2_5[1]); /* { dg-warning "specified bound 5 equals destination size" } */
|
||||
|
||||
/* Verify that copies that nul-terminate are not diagnosed. */
|
||||
CPY (dst7, "", sizeof dst7);
|
||||
CPY (dst7 + 6, "", sizeof dst7 - 6);
|
||||
CPY (dst7, "1", sizeof dst7);
|
||||
CPY (dst7 + 1, "1", sizeof dst7 - 1);
|
||||
CPY (dst7, "123456", sizeof dst7);
|
||||
CPY (dst7 + 1, "12345", sizeof dst7 - 1);
|
||||
|
||||
CPY (dst7 + i, s, 6);
|
||||
CPY (dst7 + i, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */
|
||||
/* The following two calls are diagnosed by -Wstringop-overflow. */
|
||||
CPY (dst7 + i, s, 8);
|
||||
CPY (dst7 + i, s, UR (8, 9));
|
||||
|
||||
/* No nul-termination here. */
|
||||
CPY (dst7 + 2, "12345", sizeof dst7 - 2); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */
|
||||
|
||||
/* Because strnlen appends as many NULs as necessary to write the specified
|
||||
number of byts the following doesn't (necessarily) truncate but rather
|
||||
overflow, and so is diagnosed by -Wstringop-overflow. */
|
||||
CPY (dst7, s, 8);
|
||||
|
||||
CPY (dst7 + 1, s, 6); /* { dg-warning "specified bound 6 equals destination size" } */
|
||||
CPY (dst7 + 6, s, 1); /* { dg-warning "specified bound 1 equals destination size" } */
|
||||
|
||||
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */
|
||||
CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */
|
||||
|
||||
/* The following is not yet handled. */
|
||||
CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" { xfail *-*-* } } */
|
||||
|
||||
/* Verify that a copy that nul-terminates is not diagnosed. */
|
||||
CPY (pd->a5, "1234", sizeof pd->a5);
|
||||
|
||||
/* Same above, diagnosed by -Wstringop-overflow. */
|
||||
CPY (pd->a5, s, 6);
|
||||
|
||||
/* 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" } */
|
||||
CPY (pd->c3ns, "12", 2);
|
||||
CPY (pd->c3ns, "12", 3);
|
||||
CPY (pd->c3ns, "123", 3);
|
||||
CPY (pd->c3ns, s, 3);
|
||||
CPY (pd->c3ns, s, sizeof pd->c3ns);
|
||||
|
||||
/* Verify that the idiom of calling strncpy with a bound equal to
|
||||
the size of the destination (and thus potentially without NUL-
|
||||
terminating it) immediately followed by setting the last element
|
||||
of the array to NUL is not diagnosed. */
|
||||
{
|
||||
/* This might be better written using memcpy() but it's safe so
|
||||
it probably shouldn't be diagnosed. It currently triggers
|
||||
a warning because of bug 81704. */
|
||||
strncpy (dst7, "0123456", sizeof dst7); /* { dg-bogus "truncated" "bug 81704" { xfail *-*-* } } */
|
||||
dst7[sizeof dst7 - 1] = '\0';
|
||||
sink (dst7);
|
||||
}
|
||||
|
||||
{
|
||||
const char a[] = "0123456789";
|
||||
strncpy (dst7, a, sizeof dst7);
|
||||
dst7[sizeof dst7 - 1] = '\0';
|
||||
sink (dst7);
|
||||
}
|
||||
|
||||
{
|
||||
strncpy (dst7, s, sizeof dst7);
|
||||
dst7[sizeof dst7 - 1] = '\0';
|
||||
sink (dst7);
|
||||
}
|
||||
|
||||
{
|
||||
strncpy (pd->a5, "01234", sizeof pd->a5); /* { dg-bogus "truncated" "bug 81704" { xfail *-*-* } } */
|
||||
pd->a5[sizeof pd->a5 - 1] = '\0';
|
||||
sink (pd);
|
||||
}
|
||||
|
||||
{
|
||||
strncpy (pd->a5, s, sizeof pd->a5);
|
||||
pd->a5[sizeof pd->a5 - 1] = '\0';
|
||||
sink (pd);
|
||||
}
|
||||
|
||||
{
|
||||
unsigned n = 7;
|
||||
char *p = (char*)__builtin_malloc (n);
|
||||
strncpy (p, s, n);
|
||||
p[n - 1] = '\0';
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
/* This should be diagnosed because the NUL-termination doesn't
|
||||
immediately follow the strncpy call (sink may expect pd->a5
|
||||
to be NUL-terminated). */
|
||||
strncpy (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */
|
||||
sink (pd);
|
||||
pd->a5[sizeof pd->a5] = '\0';
|
||||
sink (pd);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct Flex
|
||||
{
|
||||
size_t n;
|
||||
char a0[0];
|
||||
char ax[];
|
||||
} Flex;
|
||||
|
||||
extern char array[];
|
||||
|
||||
/* Verify that no warning is issued for array of unknown bound, flexible
|
||||
array members, or zero-length arrays, except when the source is definitely
|
||||
truncated. */
|
||||
|
||||
void test_strncpy_flexarray (Flex *pf, const char* s)
|
||||
{
|
||||
#undef CPY
|
||||
#define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
|
||||
|
||||
CPY (array, "12345", 7);
|
||||
CPY (array, "12345", 123);
|
||||
|
||||
CPY (array, s, 7);
|
||||
CPY (array, s, 123);
|
||||
|
||||
CPY (pf->a0, s, 1);
|
||||
CPY (pf->a0, s, 1234);
|
||||
|
||||
CPY (pf->a0, "", 1);
|
||||
CPY (pf->a0, "12345", 5); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */
|
||||
CPY (pf->a0, "12345", 1234);
|
||||
|
||||
CPY (pf->ax, s, 5);
|
||||
CPY (pf->ax, s, 12345);
|
||||
|
||||
CPY (pf->ax, "1234", 5);
|
||||
CPY (pf->ax, "12345", 5); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */
|
||||
CPY (pf->ax, "12345", 12345);
|
||||
}
|
||||
|
||||
/* Verify warnings for dynamically allocated objects. */
|
||||
|
||||
void test_strncpy_alloc (const char* s)
|
||||
{
|
||||
size_t n = 7;
|
||||
char *d = (char *)__builtin_malloc (n);
|
||||
|
||||
CPY (d, s, n); /* { dg-warning "specified bound 7 equals destination size" "bug 79016" { xfail *-*-* } } */
|
||||
|
||||
Dest *pd = (Dest *)__builtin_malloc (sizeof *pd * n);
|
||||
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */
|
||||
CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */
|
||||
}
|
||||
|
||||
/* Verify warnings for VLAs. */
|
||||
|
||||
void test_strncpy_vla (unsigned n, const char* s)
|
||||
{
|
||||
char vla[n];
|
||||
CPY (vla, s, 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
|
||||
CPY (vla, s, 1);
|
||||
CPY (vla, s, 2);
|
||||
CPY (vla, s, n);
|
||||
|
||||
CPY (vla, "", 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
|
||||
CPY (vla, "", 1);
|
||||
CPY (vla, S4, 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
CPY (vla, S4, n);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* Test to exercise attribute "nonstring" syntax.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wattributes" } */
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
#define NONSTR ATTR ((nonstring))
|
||||
|
||||
/* Verify it's accepted on char arrays. */
|
||||
extern NONSTR char nsx_1[];
|
||||
extern char NONSTR nsx_2[];
|
||||
extern char nsx_3[] NONSTR;
|
||||
|
||||
extern NONSTR char ns1[1];
|
||||
extern char NONSTR ns3[3];
|
||||
extern char ns5[5] NONSTR;
|
||||
|
||||
/* Verify it's accepted on char pointers. */
|
||||
extern NONSTR char* pns_1;
|
||||
extern char NONSTR* pns_2;
|
||||
extern char* NONSTR pns_3;
|
||||
|
||||
struct S
|
||||
{
|
||||
/* Verify it's accepted on char member pointers. */
|
||||
NONSTR char* mpns_1;
|
||||
char NONSTR* mpns_2;
|
||||
char* NONSTR mpns_3;
|
||||
|
||||
/* Verify it's accepted on char member arrays. */
|
||||
NONSTR char mns1[1];
|
||||
char NONSTR mns3[3];
|
||||
char mns5[5] NONSTR;
|
||||
|
||||
/* Verify it's accepted on char flexible array members. */
|
||||
char mnsx[] NONSTR;
|
||||
};
|
||||
|
||||
/* Verify it's rejected on non-array and non-pointer objects. */
|
||||
extern NONSTR char c1; /* { dg-warning ".nonstring. attribute ignored on objects of type .char." } */
|
||||
|
||||
extern NONSTR int i1; /* { dg-warning ".nonstring. attribute ignored on objects of type .int." } */
|
||||
|
||||
extern NONSTR int ia1[]; /* { dg-warning ".nonstring. attribute ignored on objects of type .int *\\\[\\\]." } */
|
||||
|
||||
extern NONSTR int* pi1; /* { dg-warning ".nonstring. attribute ignored on objects of type .int *\\*." } */
|
||||
|
||||
extern NONSTR
|
||||
void f (void); /* { dg-warning ".nonstring. attribute does not apply to functions" } */
|
||||
|
||||
struct NONSTR
|
||||
NonStrType { int i; }; /* { dg-warning ".nonstring. attribute does not apply to types" } */
|
||||
|
||||
typedef char NONSTR nschar_t; /* { dg-warning ".nonstring. attribute does not apply to types" } */
|
||||
|
||||
void func (NONSTR char *pns1, char NONSTR *pns2, char* NONSTR pns3)
|
||||
{
|
||||
(void)pns1;
|
||||
(void)pns2;
|
||||
(void)pns3;
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/* Test to exercise attribute "nonstring".
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wattributes -Wstringop-truncation -ftrack-macro-expansion=0" } */
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
#define NONSTR ATTR ((nonstring))
|
||||
#define strncpy(d, s, n) (__builtin_strncpy ((d), (s), (n)), sink (d))
|
||||
|
||||
void sink (void*);
|
||||
|
||||
/* Global string with an unknown bound. */
|
||||
extern char gsx[];
|
||||
|
||||
/* Global string with an known bound. */
|
||||
extern char gs3[3];
|
||||
|
||||
/* Global non-strings with an unknown bound. */
|
||||
extern NONSTR char gax_1[];
|
||||
extern char NONSTR gax_2[];
|
||||
extern char gax_3[] NONSTR;
|
||||
|
||||
/* Global non-strings with a known bound. */
|
||||
NONSTR char gns3[3];
|
||||
char NONSTR gns4[4];
|
||||
char gns5[5] NONSTR;
|
||||
|
||||
/* Global string pointer. */
|
||||
extern char *ps_1;
|
||||
|
||||
/* Global non-string pointers. */
|
||||
extern NONSTR char *pns_1;
|
||||
extern char* NONSTR pns_2;
|
||||
extern char *pns_3 NONSTR;
|
||||
|
||||
struct MemArrays
|
||||
{
|
||||
NONSTR char ma3[3];
|
||||
char NONSTR ma4[4];
|
||||
char ma5[5] NONSTR;
|
||||
char max[] NONSTR;
|
||||
};
|
||||
|
||||
|
||||
void test_array (const char *s, unsigned n)
|
||||
{
|
||||
const char s7[] = "1234567";
|
||||
|
||||
strncpy (gs3, "", 0); /* { dg-warning "destination unchanged after copying no bytes" } */
|
||||
strncpy (gs3, "a", 1); /* { dg-warning "output truncated before terminating nul copying 1 byte from a string of the same length" } */
|
||||
strncpy (gs3, "a", 2);
|
||||
strncpy (gs3, "a", 3);
|
||||
strncpy (gs3, "ab", 3);
|
||||
strncpy (gs3, "abc", 3); /* { dg-warning "output truncated before terminating nul copying 3 bytes from a string of the same length" } */
|
||||
|
||||
/* It might perhaps be helpful to diagnose certain truncation even
|
||||
for non-strings. Then again, since the destination has been
|
||||
explicitly annotated as non-string, it might be viewed as a false
|
||||
positive. A valid use case seen in Glibc goes something like this:
|
||||
|
||||
#if FOO
|
||||
# define S "1234"
|
||||
#else
|
||||
# define S "12345678"
|
||||
#endif
|
||||
|
||||
strncpy (d, S, 8);
|
||||
*/
|
||||
strncpy (gax_3, s7, 3);
|
||||
|
||||
strncpy (gax_1, "a", 1);
|
||||
strncpy (gax_2, "ab", 2);
|
||||
strncpy (gax_3, "abc", 3);
|
||||
strncpy (gax_3, s7, 3);
|
||||
|
||||
strncpy (gax_1, s, 1);
|
||||
strncpy (gax_2, s, 1);
|
||||
strncpy (gax_3, s, 1);
|
||||
|
||||
strncpy (gax_1, s, n);
|
||||
strncpy (gax_2, s, n);
|
||||
strncpy (gax_3, s, n);
|
||||
}
|
||||
|
||||
|
||||
void test_pointer (const char *s, unsigned n)
|
||||
{
|
||||
const char s7[] = "1234567";
|
||||
|
||||
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_1, s, 1);
|
||||
strncpy (pns_2, s, 1);
|
||||
strncpy (pns_3, s, 1);
|
||||
|
||||
strncpy (pns_1, s, n);
|
||||
strncpy (pns_2, s, n);
|
||||
strncpy (pns_3, s, n);
|
||||
}
|
||||
|
||||
|
||||
void test_member_array (struct MemArrays *p, const char *s, unsigned n)
|
||||
{
|
||||
const char s7[] = "1234567";
|
||||
|
||||
strncpy (p->ma3, "a", 1);
|
||||
strncpy (p->ma4, "ab", 2);
|
||||
strncpy (p->ma5, "abc", 3);
|
||||
strncpy (p->max, "abcd", 4);
|
||||
strncpy (p->max, s7, 5);
|
||||
|
||||
strncpy (p->ma3, s, 1);
|
||||
strncpy (p->ma4, s, 1);
|
||||
strncpy (p->ma5, s, 1);
|
||||
strncpy (p->max, s, 1);
|
||||
|
||||
strncpy (p->ma3, s7, n);
|
||||
strncpy (p->ma4, s7, n);
|
||||
strncpy (p->ma5, s7, n);
|
||||
strncpy (p->max, s7, n);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// Test -Wsizeof-pointer-memaccess warnings.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" }
|
||||
// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
|
||||
// Test just twice, once with -O0 non-fortified, once with -O2 fortified.
|
||||
// { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } }
|
||||
// { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
|
||||
|
@ -698,12 +698,17 @@ f4 (char *x, char **y, int z, char w[64])
|
|||
strncat (w, s2, sizeof (w)); // { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" }
|
||||
stpncpy (w, s1, sizeof (w)); // { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" }
|
||||
|
||||
// These are correct, no warning.
|
||||
const char s3[] = "foobarbaz";
|
||||
const char s4[] = "abcde12345678";
|
||||
strncpy (x, s3, sizeof (s3));
|
||||
strncat (x, s4, sizeof (s4));
|
||||
stpncpy (x, s3, sizeof (s3));
|
||||
|
||||
// These are pointless when the destination is large enough, and
|
||||
// cause overflow otherwise. They might as well be replaced by
|
||||
// strcpy() or memcpy().
|
||||
strncpy (x, s3, sizeof (s3)); // { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" }
|
||||
strncat (x, s4, sizeof (s4)); // { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" }
|
||||
stpncpy (x, s3, sizeof (s3)); // { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" }
|
||||
|
||||
// These are safe, no warning.
|
||||
y[1] = strndup (s3, sizeof (s3));
|
||||
z += strncmp (s3, s4, sizeof (s3));
|
||||
z += strncmp (s3, s4, sizeof (s4));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Test -Wsizeof-pointer-memaccess warnings.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" }
|
||||
// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
|
||||
// Test just twice, once with -O0 non-fortified, once with -O2 fortified,
|
||||
// suppressing buffer overflow warnings.
|
||||
// { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } }
|
||||
|
@ -703,12 +703,13 @@ f4 (char *x, char **y, int z, char w[64])
|
|||
strncat (w, s2, sizeof (w)); // { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" }
|
||||
stpncpy (w, s1, sizeof (w)); // { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" }
|
||||
|
||||
// These are correct, no warning.
|
||||
const char s3[] = "foobarbaz";
|
||||
const char s4[] = "abcde12345678";
|
||||
strncpy (x, s3, sizeof (s3));
|
||||
strncat (x, s4, sizeof (s4));
|
||||
stpncpy (x, s3, sizeof (s3));
|
||||
strncpy (x, s3, sizeof (s3)); // { dg-warning "call is the same expression as the source; did you mean to use the size of the destination" }
|
||||
strncat (x, s4, sizeof (s4)); // { dg-warning "call is the same expression as the source; did you mean to use the size of the destination" }
|
||||
stpncpy (x, s3, sizeof (s3)); // { dg-warning "call is the same expression as the source; did you mean to use the size of the destination" }
|
||||
|
||||
// These are safe, no warning.
|
||||
y[1] = strndup (s3, sizeof (s3));
|
||||
z += strncmp (s3, s4, sizeof (s3));
|
||||
z += strncmp (s3, s4, sizeof (s4));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target alloca } */
|
||||
/* { dg-options "-Walloca-larger-than=2000 -O2" } */
|
||||
/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
|
||||
|
||||
#define alloca __builtin_alloca
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* PR tree-optimization/80669 - Bad -Wstringop-overflow warnings for stpncpy
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
{ dg-options "-O2 -Wall -Wno-stringop-truncation" } */
|
||||
|
||||
#define SIZE_MAX __SIZE_MAX__
|
||||
|
||||
|
@ -18,7 +18,9 @@ size_t range (size_t min, size_t max)
|
|||
return val < min || max < val ? min : val;
|
||||
}
|
||||
|
||||
/* Verify that no warning is issued for stpncpy with constant size. */
|
||||
/* Verify that no -Wstringop-overflow warning is issued for stpncpy
|
||||
with constant size. (Some tests cause -Wstringop-truncation and
|
||||
that's expected). */
|
||||
void test_cst (char *d)
|
||||
{
|
||||
__builtin_stpncpy (d, "123", 0);
|
||||
|
@ -37,7 +39,8 @@ void test_cst (char *d)
|
|||
}
|
||||
|
||||
|
||||
/* Verify that no warning is issued for stpncpy with size in some range. */
|
||||
/* Verify that no -Wstringop-overflow warning is issued for stpncpy
|
||||
with size in some range. */
|
||||
void test_rng (char *d)
|
||||
{
|
||||
#define R(min, max) range (min, max)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* Test -Wsizeof-pointer-memaccess warnings. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
|
||||
/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
|
||||
/* Test just twice, once with -O0 non-fortified, once with -O2 fortified. */
|
||||
/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
@ -704,12 +704,17 @@ f4 (char *x, char **y, int z, char w[64])
|
|||
strncat (w, s2, sizeof (w)); /* { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" } */
|
||||
stpncpy (w, s1, sizeof (w)); /* { dg-warning "call is the same expression as the destination; did you mean to provide an explicit length" } */
|
||||
|
||||
/* These are correct, no warning. */
|
||||
/* These are pointless when the destination is large enough, and
|
||||
cause overflow otherwise. If the copies are guaranteed to be
|
||||
safe the calls might as well be replaced by strcat(), strcpy(),
|
||||
or memcpy(). */
|
||||
const char s3[] = "foobarbaz";
|
||||
const char s4[] = "abcde12345678";
|
||||
strncpy (x, s3, sizeof (s3));
|
||||
strncat (x, s4, sizeof (s4));
|
||||
stpncpy (x, s3, sizeof (s3));
|
||||
strncpy (x, s3, sizeof (s3)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
|
||||
strncat (x, s4, sizeof (s4)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
|
||||
stpncpy (x, s3, sizeof (s3)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
|
||||
|
||||
/* These are correct, no warning. */
|
||||
y[1] = strndup (s3, sizeof (s3));
|
||||
z += strncmp (s3, s4, sizeof (s3));
|
||||
z += strncmp (s3, s4, sizeof (s4));
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* { dg-do compile } */
|
||||
/* PR c/63554 - ice in "execute_todo, at passes.c:1797" with -O3
|
||||
{ dg-do compile } */
|
||||
|
||||
char *a;
|
||||
void
|
||||
|
@ -7,3 +8,5 @@ nssutil_ReadSecmodDB (void)
|
|||
long b = __builtin_object_size (0, 0);
|
||||
a = __builtin___strncat_chk (a, " ", 1, b);
|
||||
}
|
||||
|
||||
/* { dg-prune-output "\\\[-Wstringop-overflow=]" } */
|
||||
|
|
|
@ -40,12 +40,17 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "expr.h"
|
||||
#include "tree-dfa.h"
|
||||
#include "domwalk.h"
|
||||
#include "tree-ssa-alias.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "params.h"
|
||||
#include "ipa-chkp.h"
|
||||
#include "tree-hash-traits.h"
|
||||
#include "builtins.h"
|
||||
#include "target.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "diagnostic.h"
|
||||
#include "intl.h"
|
||||
#include "attribs.h"
|
||||
|
||||
/* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value
|
||||
is an index into strinfo vector, negative value stands for
|
||||
|
@ -147,6 +152,9 @@ struct decl_stridxlist_map
|
|||
mappings. */
|
||||
static hash_map<tree_decl_hash, stridxlist> *decl_to_stridxlist_htab;
|
||||
|
||||
typedef std::pair<int, location_t> stridx_strlenloc;
|
||||
static hash_map<tree, stridx_strlenloc> strlen_to_stridx;
|
||||
|
||||
/* Obstack for struct stridxlist and struct decl_stridxlist_map. */
|
||||
static struct obstack stridx_obstack;
|
||||
|
||||
|
@ -1198,6 +1206,9 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
|
|||
si->nonzero_chars = lhs;
|
||||
gcc_assert (si->full_string_p);
|
||||
}
|
||||
|
||||
location_t loc = gimple_location (stmt);
|
||||
strlen_to_stridx.put (lhs, stridx_strlenloc (idx, loc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1241,6 +1252,9 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
|
|||
strinfo *si = new_strinfo (src, idx, lhs, true);
|
||||
set_strinfo (idx, si);
|
||||
find_equal_ptrs (src, idx);
|
||||
|
||||
location_t loc = gimple_location (stmt);
|
||||
strlen_to_stridx.put (lhs, stridx_strlenloc (idx, loc));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1607,6 +1621,368 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
fprintf (dump_file, "not possible.\n");
|
||||
}
|
||||
|
||||
/* Return true if LEN depends on a call to strlen(SRC) in an interesting
|
||||
way. LEN can either be an integer expression, or a pointer (to char).
|
||||
When it is the latter (such as in recursive calls to self) is is
|
||||
assumed to be the argument in some call to strlen() whose relationship
|
||||
to SRC is being ascertained. */
|
||||
|
||||
static bool
|
||||
is_strlen_related_p (tree src, tree len)
|
||||
{
|
||||
if (TREE_CODE (TREE_TYPE (len)) == POINTER_TYPE
|
||||
&& operand_equal_p (src, len, 0))
|
||||
return true;
|
||||
|
||||
if (TREE_CODE (len) != SSA_NAME)
|
||||
return false;
|
||||
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (len);
|
||||
if (!def_stmt)
|
||||
return false;
|
||||
|
||||
if (is_gimple_call (def_stmt))
|
||||
{
|
||||
tree func = gimple_call_fndecl (def_stmt);
|
||||
if (!valid_builtin_call (def_stmt)
|
||||
|| DECL_FUNCTION_CODE (func) != BUILT_IN_STRLEN)
|
||||
return false;
|
||||
|
||||
tree arg = gimple_call_arg (def_stmt, 0);
|
||||
return is_strlen_related_p (src, arg);
|
||||
}
|
||||
|
||||
if (!is_gimple_assign (def_stmt))
|
||||
return false;
|
||||
|
||||
tree_code code = gimple_assign_rhs_code (def_stmt);
|
||||
tree rhs1 = gimple_assign_rhs1 (def_stmt);
|
||||
tree rhstype = TREE_TYPE (rhs1);
|
||||
|
||||
if ((TREE_CODE (rhstype) == POINTER_TYPE && code == POINTER_PLUS_EXPR)
|
||||
|| (INTEGRAL_TYPE_P (rhstype)
|
||||
&& (code == BIT_AND_EXPR
|
||||
|| code == NOP_EXPR)))
|
||||
{
|
||||
/* Pointer plus (an integer) and integer cast or truncation are
|
||||
considered among the (potentially) related expressions to strlen.
|
||||
Others are not. */
|
||||
return is_strlen_related_p (src, rhs1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A helper of handle_builtin_stxncpy. Check to see if the specified
|
||||
bound is a) equal to the size of the destination DST and if so, b)
|
||||
if it's immediately followed by DST[CNT - 1] = '\0'. If a) holds
|
||||
and b) does not, warn. Otherwise, do nothing. Return true if
|
||||
diagnostic has been issued.
|
||||
|
||||
The purpose is to diagnose calls to strncpy and stpncpy that do
|
||||
not nul-terminate the copy while allowing for the idiom where
|
||||
such a call is immediately followed by setting the last element
|
||||
to nul, as in:
|
||||
char a[32];
|
||||
strncpy (a, s, sizeof a);
|
||||
a[sizeof a - 1] = '\0';
|
||||
*/
|
||||
|
||||
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];
|
||||
|
||||
if (TREE_CODE (cnt) == INTEGER_CST)
|
||||
cntrange[0] = cntrange[1] = wi::to_wide (cnt);
|
||||
else if (TREE_CODE (cnt) == SSA_NAME)
|
||||
{
|
||||
enum value_range_type rng = get_range_info (cnt, cntrange, cntrange + 1);
|
||||
if (rng == VR_RANGE)
|
||||
;
|
||||
else if (rng == VR_ANTI_RANGE)
|
||||
{
|
||||
wide_int maxobjsize = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
|
||||
|
||||
if (wi::ltu_p (cntrange[1], maxobjsize))
|
||||
{
|
||||
cntrange[0] = cntrange[1] + 1;
|
||||
cntrange[1] = maxobjsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
cntrange[1] = cntrange[0] - 1;
|
||||
cntrange[0] = wi::zero (TYPE_PRECISION (TREE_TYPE (cnt)));
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
/* Negative value is the constant string length. If it's less than
|
||||
the lower bound there is no truncation. */
|
||||
int sidx = get_stridx (src);
|
||||
if (sidx < 0 && wi::gtu_p (cntrange[0], ~sidx))
|
||||
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;
|
||||
}
|
||||
|
||||
/* Look for dst[i] = '\0'; after the stxncpy() call and if found
|
||||
avoid the truncation warning. */
|
||||
gsi_next (&gsi);
|
||||
gimple *next_stmt = gsi_stmt (gsi);
|
||||
|
||||
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))
|
||||
return false;
|
||||
}
|
||||
|
||||
int prec = TYPE_PRECISION (TREE_TYPE (cnt));
|
||||
wide_int lenrange[2];
|
||||
if (strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL)
|
||||
{
|
||||
lenrange[0] = (sisrc->nonzero_chars
|
||||
&& TREE_CODE (sisrc->nonzero_chars) == INTEGER_CST
|
||||
? wi::to_wide (sisrc->nonzero_chars)
|
||||
: wi::zero (prec));
|
||||
lenrange[1] = lenrange[0];
|
||||
}
|
||||
else if (sidx < 0)
|
||||
lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
|
||||
else
|
||||
{
|
||||
tree range[2];
|
||||
get_range_strlen (src, range);
|
||||
if (range[0])
|
||||
{
|
||||
lenrange[0] = wi::to_wide (range[0], prec);
|
||||
lenrange[1] = wi::to_wide (range[1], prec);
|
||||
}
|
||||
else
|
||||
{
|
||||
lenrange[0] = wi::shwi (0, prec);
|
||||
lenrange[1] = wi::shwi (-1, prec);
|
||||
}
|
||||
}
|
||||
|
||||
location_t callloc = gimple_location (stmt);
|
||||
tree func = gimple_call_fndecl (stmt);
|
||||
|
||||
if (lenrange[0] != 0 || !wi::neg_p (lenrange[1]))
|
||||
{
|
||||
/* If the longest source string is shorter than the lower bound
|
||||
of the specified count the copy is definitely nul-terminated. */
|
||||
if (wi::ltu_p (lenrange[1], cntrange[0]))
|
||||
return false;
|
||||
|
||||
if (wi::neg_p (lenrange[1]))
|
||||
{
|
||||
/* The length of one of the strings is unknown but at least
|
||||
one has non-zero length and that length is stored in
|
||||
LENRANGE[1]. Swap the bounds to force a "may be truncated"
|
||||
warning below. */
|
||||
lenrange[1] = lenrange[0];
|
||||
lenrange[0] = wi::shwi (0, prec);
|
||||
}
|
||||
|
||||
if (wi::geu_p (lenrange[0], cntrange[1]))
|
||||
{
|
||||
/* The shortest string is longer than the upper bound of
|
||||
the count so the truncation is certain. */
|
||||
if (cntrange[0] == cntrange[1])
|
||||
return warning_at (callloc, OPT_Wstringop_truncation,
|
||||
integer_onep (cnt)
|
||||
? G_("%qD output truncated copying %E byte "
|
||||
"from a string of length %wu")
|
||||
: G_("%qD output truncated copying %E bytes "
|
||||
"from a string of length %wu"),
|
||||
func, cnt, lenrange[0].to_uhwi ());
|
||||
|
||||
return warning_at (callloc, OPT_Wstringop_truncation,
|
||||
"%qD output truncated copying between %wu "
|
||||
"and %wu bytes from a string of length %wu",
|
||||
func, cntrange[0].to_uhwi (),
|
||||
cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
|
||||
}
|
||||
else if (wi::geu_p (lenrange[1], cntrange[1]))
|
||||
{
|
||||
/* The longest string is longer than the upper bound of
|
||||
the count so the truncation is possible. */
|
||||
if (cntrange[0] == cntrange[1])
|
||||
return warning_at (callloc, OPT_Wstringop_truncation,
|
||||
integer_onep (cnt)
|
||||
? G_("%qD output may be truncated copying %E "
|
||||
"byte from a string of length %wu")
|
||||
: G_("%qD output may be truncated copying %E "
|
||||
"bytes from a string of length %wu"),
|
||||
func, cnt, lenrange[1].to_uhwi ());
|
||||
|
||||
return warning_at (callloc, OPT_Wstringop_truncation,
|
||||
"%qD output may be truncated copying between %wu "
|
||||
"and %wu bytes from a string of length %wu",
|
||||
func, cntrange[0].to_uhwi (),
|
||||
cntrange[1].to_uhwi (), lenrange[1].to_uhwi ());
|
||||
}
|
||||
|
||||
if (cntrange[0] != cntrange[1]
|
||||
&& wi::leu_p (cntrange[0], lenrange[0])
|
||||
&& wi::leu_p (cntrange[1], lenrange[0] + 1))
|
||||
{
|
||||
/* If the source (including the terminating nul) is longer than
|
||||
the lower bound of the specified count but shorter than the
|
||||
upper bound the copy may (but need not) be truncated. */
|
||||
return warning_at (callloc, OPT_Wstringop_truncation,
|
||||
"%qD output may be truncated copying between %wu "
|
||||
"and %wu bytes from a string of length %wu",
|
||||
func, cntrange[0].to_uhwi (),
|
||||
cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
|
||||
}
|
||||
}
|
||||
|
||||
if (tree dstsize = compute_objsize (dst, 1))
|
||||
{
|
||||
/* The source length is uknown. Try to determine the destination
|
||||
size and see if it matches the specified bound. If not, bail.
|
||||
Otherwise go on to see if it should be diagnosed for possible
|
||||
truncation. */
|
||||
if (!dstsize)
|
||||
return false;
|
||||
|
||||
if (wi::to_wide (dstsize) != cntrange[1])
|
||||
return false;
|
||||
|
||||
if (cntrange[0] == cntrange[1])
|
||||
return warning_at (callloc, OPT_Wstringop_truncation,
|
||||
"%qD specified bound %E equals destination size",
|
||||
func, cnt);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check the size argument to the built-in forms of stpncpy and strncpy
|
||||
to see if it's derived from calling strlen() on the source argument
|
||||
and if so, issue a warning. */
|
||||
|
||||
static void
|
||||
handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
|
||||
{
|
||||
gimple *stmt = gsi_stmt (*gsi);
|
||||
|
||||
bool with_bounds = gimple_call_with_bounds_p (stmt);
|
||||
|
||||
tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
|
||||
tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2);
|
||||
|
||||
/* 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);
|
||||
if (!pss || pss->first <= 0)
|
||||
{
|
||||
if (maybe_diag_stxncpy_trunc (*gsi, src, len))
|
||||
gimple_set_no_warning (stmt, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int sidx = get_stridx (src);
|
||||
strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
|
||||
|
||||
/* Strncpy() et al. cannot modify the source string. Prevent the rest
|
||||
of the pass from invalidating the strinfo data. */
|
||||
if (sisrc)
|
||||
sisrc->dont_invalidate = true;
|
||||
|
||||
/* Retrieve the strinfo data for the string S that LEN was computed
|
||||
from as some function F of strlen (S) (i.e., LEN need not be equal
|
||||
to strlen(S)). */
|
||||
strinfo *silen = get_strinfo (pss->first);
|
||||
|
||||
location_t callloc = gimple_location (stmt);
|
||||
|
||||
tree func = gimple_call_fndecl (stmt);
|
||||
|
||||
bool warned = false;
|
||||
|
||||
/* When -Wstringop-truncation is set, try to determine truncation
|
||||
before diagnosing possible overflow. Truncation is implied by
|
||||
the LEN argument being equal to strlen(SRC), regardless of
|
||||
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);
|
||||
else if (silen && is_strlen_related_p (src, silen->ptr))
|
||||
warned = warning_at (callloc, OPT_Wstringop_overflow_,
|
||||
"%qD specified bound depends on the length "
|
||||
"of the source argument", func);
|
||||
if (warned)
|
||||
{
|
||||
location_t strlenloc = pss->second;
|
||||
if (strlenloc != UNKNOWN_LOCATION && strlenloc != callloc)
|
||||
inform (strlenloc, "length computed here");
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the size argument to the built-in forms of strncat to see if
|
||||
it's derived from calling strlen() on the source argument and if so,
|
||||
issue a warning. */
|
||||
|
||||
static void
|
||||
handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
|
||||
{
|
||||
/* Same as stxncpy(). */
|
||||
handle_builtin_stxncpy (bcode, gsi);
|
||||
}
|
||||
|
||||
/* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
|
||||
If strlen of the second argument is known and length of the third argument
|
||||
is that plus one, strlen of the first argument is the same after this
|
||||
|
@ -2513,6 +2889,19 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
|
|||
case BUILT_IN_STPCPY_CHK_CHKP:
|
||||
handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNCAT:
|
||||
case BUILT_IN_STRNCAT_CHK:
|
||||
handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
|
||||
break;
|
||||
|
||||
case BUILT_IN_STPNCPY:
|
||||
case BUILT_IN_STPNCPY_CHK:
|
||||
case BUILT_IN_STRNCPY:
|
||||
case BUILT_IN_STRNCPY_CHK:
|
||||
handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
|
||||
break;
|
||||
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMCPY_CHK:
|
||||
case BUILT_IN_MEMPCPY:
|
||||
|
@ -2576,6 +2965,10 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
|
|||
else if (code == EQ_EXPR || code == NE_EXPR)
|
||||
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, *ps);
|
||||
}
|
||||
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
|
||||
{
|
||||
|
@ -2827,6 +3220,8 @@ pass_strlen::execute (function *fun)
|
|||
laststmt.len = NULL_TREE;
|
||||
laststmt.stridx = 0;
|
||||
|
||||
strlen_to_stridx.empty ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue