Move more code to new gimple-ssa-warn-access pass.
gcc/ChangeLog: * builtins.c (expand_builtin_memchr): Move to gimple-ssa-warn-access.cc. (expand_builtin_strcat): Same. (expand_builtin_stpncpy): Same. (expand_builtin_strncat): Same. (check_read_access): Same. (check_memop_access): Same. (expand_builtin_strlen): Move checks to gimple-ssa-warn-access.cc. (expand_builtin_strnlen): Same. (expand_builtin_memcpy): Same. (expand_builtin_memmove): Same. (expand_builtin_mempcpy): Same. (expand_builtin_strcpy): Same. (expand_builtin_strcpy_args): Same. (expand_builtin_stpcpy_1): Same. (expand_builtin_strncpy): Same. (expand_builtin_memset): Same. (expand_builtin_bzero): Same. (expand_builtin_strcmp): Same. (expand_builtin_strncmp): Same. (expand_builtin): Remove handlers. (fold_builtin_strlen): Add a comment. * builtins.h (check_access): Move to gimple-ssa-warn-access.cc. * calls.c (maybe_warn_nonstring_arg): Same. * diagnostic-spec.c (nowarn_spec_t::nowarn_spec_t): Add warning option. * gimple-fold.c (gimple_fold_builtin_strcpy): Pass argument to callee. (gimple_fold_builtin_stpcpy): Same. * gimple-ssa-warn-access.cc (has_location): New function. (get_location): Same. (get_callee_fndecl): Same. (call_nargs): Same. (call_arg): Same. (warn_string_no_nul): Define. (unterminated_array): Same. (check_nul_terminated_array): Same. (maybe_warn_nonstring_arg): Same. (maybe_warn_for_bound): Same. (warn_for_access): Same. (check_access): Same. (check_memop_access): Same. (check_read_access): Same. (warn_dealloc_offset): Use helper functions. (maybe_emit_free_warning): Same. (class pass_waccess): Add members. (check_strcat): New function. (check_strncat): New function. (check_stxcpy): New function. (check_stxncpy): New function. (check_strncmp): New function. (pass_waccess::check_builtin): New function. (pass_waccess::check): Call it. * gimple-ssa-warn-access.h (warn_string_no_nul): Move here from builtins.h. (maybe_warn_for_bound): Same. (check_access): Same. (check_memop_access): Same. (check_read_access): Same. * pointer-query.h (struct access_data): Define a ctor overload. gcc/testsuite/ChangeLog: * c-c++-common/Wsizeof-pointer-memaccess1.c: Also disable -Wstringop-overread. * c-c++-common/attr-nonstring-3.c: Adjust pattern of expected message. * gcc.dg/Warray-bounds-39.c: Add an xfail due to a known bug. * gcc.dg/Wstring-compare-3.c: Also disable -Wstringop-overread. * gcc.dg/attr-nonstring-2.c: Adjust pattern of expected message. * gcc.dg/attr-nonstring-4.c: Same. * gcc.dg/Wstringop-overread-6.c: New test. * gcc.dg/sso-14.c: Fix typos to avoid buffer overflow.
This commit is contained in:
parent
629b5699fb
commit
81d6cdd335
16 changed files with 1661 additions and 840 deletions
399
gcc/builtins.c
399
gcc/builtins.c
|
@ -131,7 +131,6 @@ static rtx expand_builtin_va_copy (tree);
|
|||
static rtx inline_expand_builtin_bytecmp (tree, rtx);
|
||||
static rtx expand_builtin_strcmp (tree, rtx);
|
||||
static rtx expand_builtin_strncmp (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_memchr (tree, rtx);
|
||||
static rtx expand_builtin_memcpy (tree, rtx);
|
||||
static rtx expand_builtin_memory_copy_args (tree dest, tree src, tree len,
|
||||
rtx target, tree exp,
|
||||
|
@ -140,12 +139,9 @@ static rtx expand_builtin_memory_copy_args (tree dest, tree src, tree len,
|
|||
static rtx expand_builtin_memmove (tree, rtx);
|
||||
static rtx expand_builtin_mempcpy (tree, rtx);
|
||||
static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx, tree, memop_ret);
|
||||
static rtx expand_builtin_strcat (tree);
|
||||
static rtx expand_builtin_strcpy (tree, rtx);
|
||||
static rtx expand_builtin_strcpy_args (tree, tree, tree, rtx);
|
||||
static rtx expand_builtin_stpcpy (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_stpncpy (tree, rtx);
|
||||
static rtx expand_builtin_strncat (tree, rtx);
|
||||
static rtx expand_builtin_strncpy (tree, rtx);
|
||||
static rtx expand_builtin_memset (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree);
|
||||
|
@ -186,7 +182,6 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
|
|||
static void maybe_emit_chk_warning (tree, enum built_in_function);
|
||||
static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
|
||||
static tree fold_builtin_object_size (tree, tree);
|
||||
static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
|
||||
|
||||
unsigned HOST_WIDE_INT target_newline;
|
||||
unsigned HOST_WIDE_INT target_percent;
|
||||
|
@ -2957,8 +2952,6 @@ expand_builtin_strlen (tree exp, rtx target,
|
|||
return NULL_RTX;
|
||||
|
||||
tree src = CALL_EXPR_ARG (exp, 0);
|
||||
if (!check_read_access (exp, src))
|
||||
return NULL_RTX;
|
||||
|
||||
/* If the length can be computed at compile-time, return it. */
|
||||
if (tree len = c_strlen (src, 0))
|
||||
|
@ -3062,8 +3055,6 @@ expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
|
|||
if (!bound)
|
||||
return NULL_RTX;
|
||||
|
||||
check_read_access (exp, src, bound);
|
||||
|
||||
location_t loc = UNKNOWN_LOCATION;
|
||||
if (EXPR_HAS_LOCATION (exp))
|
||||
loc = EXPR_LOCATION (exp);
|
||||
|
@ -3201,65 +3192,6 @@ determine_block_size (tree len, rtx len_rtx,
|
|||
GET_MODE_MASK (GET_MODE (len_rtx)));
|
||||
}
|
||||
|
||||
/* A convenience wrapper for check_access above to check access
|
||||
by a read-only function like puts. */
|
||||
|
||||
static bool
|
||||
check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
|
||||
int ost /* = 1 */)
|
||||
{
|
||||
if (!warn_stringop_overread)
|
||||
return true;
|
||||
|
||||
if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
|
||||
bound = fold_convert (size_type_node, bound);
|
||||
access_data data (exp, access_read_only, NULL_TREE, false, bound, true);
|
||||
compute_objsize (src, ost, &data.src);
|
||||
return check_access (exp, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
|
||||
/*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
|
||||
&data);
|
||||
}
|
||||
|
||||
/* Helper to determine and check the sizes of the source and the destination
|
||||
of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls. EXP is the
|
||||
call expression, DEST is the destination argument, SRC is the source
|
||||
argument or null, and LEN is the number of bytes. Use Object Size type-0
|
||||
regardless of the OPT_Wstringop_overflow_ setting. Return true on success
|
||||
(no overflow or invalid sizes), false otherwise. */
|
||||
|
||||
static bool
|
||||
check_memop_access (tree exp, tree dest, tree src, tree size)
|
||||
{
|
||||
/* For functions like memset and memcpy that operate on raw memory
|
||||
try to determine the size of the largest source and destination
|
||||
object using type-0 Object Size regardless of the object size
|
||||
type specified by the option. */
|
||||
access_data data (exp, access_read_write);
|
||||
tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
|
||||
tree dstsize = compute_objsize (dest, 0, &data.dst);
|
||||
|
||||
return check_access (exp, size, /*maxread=*/NULL_TREE,
|
||||
srcsize, dstsize, data.mode, &data);
|
||||
}
|
||||
|
||||
/* Validate memchr arguments without performing any expansion.
|
||||
Return NULL_RTX. */
|
||||
|
||||
static rtx
|
||||
expand_builtin_memchr (tree exp, rtx)
|
||||
{
|
||||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
|
||||
tree arg1 = CALL_EXPR_ARG (exp, 0);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
check_read_access (exp, arg1, len, 0);
|
||||
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Expand a call EXP to the memcpy builtin.
|
||||
Return NULL_RTX if we failed, the caller should emit a normal call,
|
||||
otherwise try to get the result in TARGET, if convenient (and in
|
||||
|
@ -3276,8 +3208,6 @@ expand_builtin_memcpy (tree exp, rtx target)
|
|||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
check_memop_access (exp, dest, src, len);
|
||||
|
||||
return expand_builtin_memory_copy_args (dest, src, len, target, exp,
|
||||
/*retmode=*/ RETURN_BEGIN, false);
|
||||
}
|
||||
|
@ -3296,8 +3226,6 @@ expand_builtin_memmove (tree exp, rtx target)
|
|||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
check_memop_access (exp, dest, src, len);
|
||||
|
||||
return expand_builtin_memory_copy_args (dest, src, len, target, exp,
|
||||
/*retmode=*/ RETURN_BEGIN, true);
|
||||
}
|
||||
|
@ -3334,8 +3262,6 @@ expand_builtin_mempcpy (tree exp, rtx target)
|
|||
/* Avoid expanding mempcpy into memcpy when the call is determined
|
||||
to overflow the buffer. This also prevents the same overflow
|
||||
from being diagnosed again when expanding memcpy. */
|
||||
if (!check_memop_access (exp, dest, src, len))
|
||||
return NULL_RTX;
|
||||
|
||||
return expand_builtin_mempcpy_args (dest, src, len,
|
||||
target, exp, /*retmode=*/ RETURN_END);
|
||||
|
@ -3511,36 +3437,6 @@ expand_movstr (tree dest, tree src, rtx target, memop_ret retmode)
|
|||
return target;
|
||||
}
|
||||
|
||||
/* Do some very basic size validation of a call to the strcpy builtin
|
||||
given by EXP. Return NULL_RTX to have the built-in expand to a call
|
||||
to the library function. */
|
||||
|
||||
static rtx
|
||||
expand_builtin_strcat (tree exp)
|
||||
{
|
||||
if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
|
||||
|| !warn_stringop_overflow)
|
||||
return NULL_RTX;
|
||||
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
/* There is no way here to determine the length of the string in
|
||||
the destination to which the SRC string is being appended so
|
||||
just diagnose cases when the souce string is longer than
|
||||
the destination object. */
|
||||
access_data data (exp, access_read_write, NULL_TREE, true,
|
||||
NULL_TREE, true);
|
||||
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
|
||||
compute_objsize (src, ost, &data.src);
|
||||
tree destsize = compute_objsize (dest, ost, &data.dst);
|
||||
|
||||
check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
|
||||
src, destsize, data.mode, &data);
|
||||
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Expand expression EXP, which is a call to the strcpy builtin. Return
|
||||
NULL_RTX if we failed the caller should emit a normal call, otherwise
|
||||
try to get the result in TARGET, if convenient (and in mode MODE if that's
|
||||
|
@ -3555,29 +3451,7 @@ expand_builtin_strcpy (tree exp, rtx target)
|
|||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
if (warn_stringop_overflow)
|
||||
{
|
||||
access_data data (exp, access_read_write, NULL_TREE, true,
|
||||
NULL_TREE, true);
|
||||
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
|
||||
compute_objsize (src, ost, &data.src);
|
||||
tree dstsize = compute_objsize (dest, ost, &data.dst);
|
||||
check_access (exp, /*dstwrite=*/ NULL_TREE,
|
||||
/*maxread=*/ NULL_TREE, /*srcstr=*/ src,
|
||||
dstsize, data.mode, &data);
|
||||
}
|
||||
|
||||
if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
|
||||
{
|
||||
/* 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. */
|
||||
tree fndecl = get_callee_fndecl (exp);
|
||||
maybe_warn_nonstring_arg (fndecl, exp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL_RTX;
|
||||
return expand_builtin_strcpy_args (exp, dest, src, target);
|
||||
}
|
||||
|
||||
/* Helper function to do the actual work for expand_builtin_strcpy. The
|
||||
|
@ -3587,19 +3461,8 @@ expand_builtin_strcpy (tree exp, rtx target)
|
|||
expand_builtin_strcpy. */
|
||||
|
||||
static rtx
|
||||
expand_builtin_strcpy_args (tree exp, tree dest, tree src, rtx target)
|
||||
expand_builtin_strcpy_args (tree, tree dest, tree src, rtx target)
|
||||
{
|
||||
/* Detect strcpy calls with unterminated arrays.. */
|
||||
tree size;
|
||||
bool exact;
|
||||
if (tree nonstr = unterminated_array (src, &size, &exact))
|
||||
{
|
||||
/* NONSTR refers to the non-nul terminated constant array. */
|
||||
warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, nonstr,
|
||||
size, exact);
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
return expand_movstr (dest, src, target, /*retmode=*/ RETURN_BEGIN);
|
||||
}
|
||||
|
||||
|
@ -3620,15 +3483,6 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
|
|||
dst = CALL_EXPR_ARG (exp, 0);
|
||||
src = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
if (warn_stringop_overflow)
|
||||
{
|
||||
access_data data (exp, access_read_write);
|
||||
tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
|
||||
&data.dst);
|
||||
check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
|
||||
src, destsize, data.mode, &data);
|
||||
}
|
||||
|
||||
/* If return value is ignored, transform stpcpy into strcpy. */
|
||||
if (target == const0_rtx && builtin_decl_implicit (BUILT_IN_STRCPY))
|
||||
{
|
||||
|
@ -3651,9 +3505,6 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
|
|||
return expand_movstr (dst, src, target,
|
||||
/*retmode=*/ RETURN_END_MINUS_ONE);
|
||||
|
||||
if (lendata.decl)
|
||||
warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, lendata.decl);
|
||||
|
||||
lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
|
||||
ret = expand_builtin_mempcpy_args (dst, src, lenp1,
|
||||
target, exp,
|
||||
|
@ -3715,30 +3566,6 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
|
|||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Check a call EXP to the stpncpy built-in for validity.
|
||||
Return NULL_RTX on both success and failure. */
|
||||
|
||||
static rtx
|
||||
expand_builtin_stpncpy (tree exp, rtx)
|
||||
{
|
||||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
|
||||
|| !warn_stringop_overflow)
|
||||
return NULL_RTX;
|
||||
|
||||
/* The source and destination of the call. */
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
/* The exact number of bytes to write (not the maximum). */
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
access_data data (exp, access_read_write);
|
||||
/* The size of the destination object. */
|
||||
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
|
||||
check_access (exp, len, /*maxread=*/len, src, destsize, data.mode, &data);
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE)
|
||||
bytes from constant string DATA + OFFSET and return it as target
|
||||
constant. */
|
||||
|
@ -3817,78 +3644,6 @@ check_strncat_sizes (tree exp, tree objsize)
|
|||
objsize, data.mode, &data);
|
||||
}
|
||||
|
||||
/* Similar to expand_builtin_strcat, do some very basic size validation
|
||||
of a call to the strcpy builtin given by EXP. Return NULL_RTX to have
|
||||
the built-in expand to a call to the library function. */
|
||||
|
||||
static rtx
|
||||
expand_builtin_strncat (tree exp, rtx)
|
||||
{
|
||||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
|
||||
|| !warn_stringop_overflow)
|
||||
return NULL_RTX;
|
||||
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
/* The upper bound on the number of bytes to write. */
|
||||
tree maxread = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
/* Detect unterminated source (only). */
|
||||
if (!check_nul_terminated_array (exp, src, maxread))
|
||||
return NULL_RTX;
|
||||
|
||||
/* The length of the source sequence. */
|
||||
tree slen = c_strlen (src, 1);
|
||||
|
||||
/* Try to determine the range of lengths that the source expression
|
||||
refers to. Since the lengths are only used for warning and not
|
||||
for code generation disable strict mode below. */
|
||||
tree maxlen = slen;
|
||||
if (!maxlen)
|
||||
{
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (src, &lendata, /* eltsize = */ 1);
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
|
||||
access_data data (exp, access_read_write);
|
||||
/* Try to verify that the destination is big enough for the shortest
|
||||
string. First try to determine the size of the destination object
|
||||
into which the source is being copied. */
|
||||
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
|
||||
|
||||
/* Add one for the terminating nul. */
|
||||
tree srclen = (maxlen
|
||||
? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
|
||||
size_one_node)
|
||||
: NULL_TREE);
|
||||
|
||||
/* The strncat function copies at most MAXREAD bytes and always appends
|
||||
the terminating nul so the specified upper bound should never be equal
|
||||
to (or greater than) the size of the destination. */
|
||||
if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
|
||||
&& tree_int_cst_equal (destsize, maxread))
|
||||
{
|
||||
location_t loc = EXPR_LOCATION (exp);
|
||||
warning_at (loc, OPT_Wstringop_overflow_,
|
||||
"%qD specified bound %E equals destination size",
|
||||
get_callee_fndecl (exp), maxread);
|
||||
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
if (!srclen
|
||||
|| (maxread && tree_fits_uhwi_p (maxread)
|
||||
&& tree_fits_uhwi_p (srclen)
|
||||
&& tree_int_cst_lt (maxread, srclen)))
|
||||
srclen = maxread;
|
||||
|
||||
check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
|
||||
destsize, data.mode, &data);
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Expand expression EXP, which is a call to the strncpy builtin. Return
|
||||
NULL_RTX if we failed the caller should emit a normal call. */
|
||||
|
||||
|
@ -3908,18 +3663,6 @@ expand_builtin_strncpy (tree exp, rtx target)
|
|||
/* The length of the source sequence. */
|
||||
tree slen = c_strlen (src, 1);
|
||||
|
||||
if (warn_stringop_overflow)
|
||||
{
|
||||
access_data data (exp, access_read_write, len, true, len, true);
|
||||
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
|
||||
compute_objsize (src, ost, &data.src);
|
||||
tree dstsize = compute_objsize (dest, ost, &data.dst);
|
||||
/* The number of bytes to write is LEN but check_access will also
|
||||
check SLEN if LEN's value isn't known. */
|
||||
check_access (exp, /*dstwrite=*/len,
|
||||
/*maxread=*/len, src, dstsize, data.mode, &data);
|
||||
}
|
||||
|
||||
/* We must be passed a constant len and src parameter. */
|
||||
if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
|
||||
return NULL_RTX;
|
||||
|
@ -4141,8 +3884,6 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
|
|||
tree val = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
check_memop_access (exp, dest, NULL_TREE, len);
|
||||
|
||||
return expand_builtin_memset_args (dest, val, len, target, mode, exp);
|
||||
}
|
||||
|
||||
|
@ -4470,8 +4211,6 @@ expand_builtin_bzero (tree exp)
|
|||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree size = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
check_memop_access (exp, dest, NULL_TREE, size);
|
||||
|
||||
/* New argument list transforming bzero(ptr x, int y) to
|
||||
memset(ptr x, int 0, size_t y). This is done this way
|
||||
so that if it isn't expanded inline, we fallback to
|
||||
|
@ -4622,10 +4361,6 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
|
|||
tree arg1 = CALL_EXPR_ARG (exp, 0);
|
||||
tree arg2 = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
if (!check_read_access (exp, arg1)
|
||||
|| !check_read_access (exp, arg2))
|
||||
return NULL_RTX;
|
||||
|
||||
/* Due to the performance benefit, always inline the calls first. */
|
||||
rtx result = NULL_RTX;
|
||||
result = inline_expand_builtin_bytecmp (exp, target);
|
||||
|
@ -4707,11 +4442,6 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
|
|||
tree fndecl = get_callee_fndecl (exp);
|
||||
if (result)
|
||||
{
|
||||
/* 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 (fndecl, exp);
|
||||
|
||||
/* Return the value in the proper mode for this function. */
|
||||
machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
|
||||
if (GET_MODE (result) == mode)
|
||||
|
@ -4725,6 +4455,7 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
|
|||
/* Expand the library call ourselves using a stabilized argument
|
||||
list to avoid re-evaluating the function's arguments twice. */
|
||||
tree fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 2, arg1, arg2);
|
||||
copy_warning (fn, exp);
|
||||
gcc_assert (TREE_CODE (fn) == CALL_EXPR);
|
||||
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
|
||||
return expand_call (fn, target, target == const0_rtx);
|
||||
|
@ -4746,66 +4477,10 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
|
|||
tree arg2 = CALL_EXPR_ARG (exp, 1);
|
||||
tree arg3 = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
if (!check_nul_terminated_array (exp, arg1, arg3)
|
||||
|| !check_nul_terminated_array (exp, arg2, arg3))
|
||||
return NULL_RTX;
|
||||
|
||||
location_t loc = EXPR_LOCATION (exp);
|
||||
tree len1 = c_strlen (arg1, 1);
|
||||
tree len2 = c_strlen (arg2, 1);
|
||||
|
||||
if (!len1 || !len2)
|
||||
{
|
||||
/* 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. */
|
||||
if (!maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp)
|
||||
&& !len1 && !len2)
|
||||
{
|
||||
/* A strncmp read is constrained not just by the bound but
|
||||
also by the length of the shorter string. Specifying
|
||||
a bound that's larger than the size of either array makes
|
||||
no sense and is likely a bug. When the length of neither
|
||||
of the two strings is known but the sizes of both of
|
||||
the arrays they are stored in is, issue a warning if
|
||||
the bound is larger than than the size of the larger
|
||||
of the two arrays. */
|
||||
|
||||
access_ref ref1 (arg3, true);
|
||||
access_ref ref2 (arg3, true);
|
||||
|
||||
tree bndrng[2] = { NULL_TREE, NULL_TREE };
|
||||
get_size_range (arg3, bndrng, ref1.bndrng);
|
||||
|
||||
tree size1 = compute_objsize (arg1, 1, &ref1);
|
||||
tree size2 = compute_objsize (arg2, 1, &ref2);
|
||||
tree func = get_callee_fndecl (exp);
|
||||
|
||||
if (size1 && size2 && bndrng[0] && !integer_zerop (bndrng[0]))
|
||||
{
|
||||
offset_int rem1 = ref1.size_remaining ();
|
||||
offset_int rem2 = ref2.size_remaining ();
|
||||
if (rem1 == 0 || rem2 == 0)
|
||||
maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
|
||||
bndrng, integer_zero_node);
|
||||
else
|
||||
{
|
||||
offset_int maxrem = wi::max (rem1, rem2, UNSIGNED);
|
||||
if (maxrem < wi::to_offset (bndrng[0]))
|
||||
maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp,
|
||||
func, bndrng,
|
||||
wide_int_to_tree (sizetype, maxrem));
|
||||
}
|
||||
}
|
||||
else if (bndrng[0]
|
||||
&& !integer_zerop (bndrng[0])
|
||||
&& ((size1 && integer_zerop (size1))
|
||||
|| (size2 && integer_zerop (size2))))
|
||||
maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
|
||||
bndrng, integer_zero_node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Due to the performance benefit, always inline the calls first. */
|
||||
rtx result = NULL_RTX;
|
||||
result = inline_expand_builtin_bytecmp (exp, target);
|
||||
|
@ -7544,63 +7219,12 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
|||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRCAT:
|
||||
target = expand_builtin_strcat (exp);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_GETTEXT:
|
||||
case BUILT_IN_PUTS:
|
||||
case BUILT_IN_PUTS_UNLOCKED:
|
||||
case BUILT_IN_STRDUP:
|
||||
if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
|
||||
check_read_access (exp, CALL_EXPR_ARG (exp, 0));
|
||||
break;
|
||||
|
||||
case BUILT_IN_INDEX:
|
||||
case BUILT_IN_RINDEX:
|
||||
case BUILT_IN_STRCHR:
|
||||
case BUILT_IN_STRRCHR:
|
||||
if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
check_read_access (exp, CALL_EXPR_ARG (exp, 0));
|
||||
break;
|
||||
|
||||
case BUILT_IN_FPUTS:
|
||||
case BUILT_IN_FPUTS_UNLOCKED:
|
||||
if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
|
||||
check_read_access (exp, CALL_EXPR_ARG (exp, 0));
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNDUP:
|
||||
if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
check_read_access (exp, CALL_EXPR_ARG (exp, 0), CALL_EXPR_ARG (exp, 1));
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRCASECMP:
|
||||
case BUILT_IN_STRPBRK:
|
||||
case BUILT_IN_STRSPN:
|
||||
case BUILT_IN_STRCSPN:
|
||||
case BUILT_IN_STRSTR:
|
||||
if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
|
||||
{
|
||||
check_read_access (exp, CALL_EXPR_ARG (exp, 0));
|
||||
check_read_access (exp, CALL_EXPR_ARG (exp, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRCPY:
|
||||
target = expand_builtin_strcpy (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNCAT:
|
||||
target = expand_builtin_strncat (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNCPY:
|
||||
target = expand_builtin_strncpy (exp, target);
|
||||
if (target)
|
||||
|
@ -7613,18 +7237,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
|||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STPNCPY:
|
||||
target = expand_builtin_stpncpy (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_MEMCHR:
|
||||
target = expand_builtin_memchr (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_MEMCPY:
|
||||
target = expand_builtin_memcpy (exp, target);
|
||||
if (target)
|
||||
|
@ -8626,8 +8238,11 @@ fold_builtin_strlen (location_t loc, tree expr, tree type, tree arg)
|
|||
if (len)
|
||||
return fold_convert_loc (loc, type, len);
|
||||
|
||||
/* TODO: Move this to gimple-ssa-warn-access once the pass runs
|
||||
also early enough to detect invalid reads in multimensional
|
||||
arrays and struct members. */
|
||||
if (!lendata.decl)
|
||||
c_strlen (arg, 1, &lendata);
|
||||
c_strlen (arg, 1, &lendata);
|
||||
|
||||
if (lendata.decl)
|
||||
{
|
||||
|
|
|
@ -151,8 +151,4 @@ extern internal_fn replacement_internal_fn (gcall *);
|
|||
|
||||
extern bool builtin_with_linkage_p (tree);
|
||||
|
||||
class access_data;
|
||||
extern bool check_access (tree, tree, tree, tree, tree,
|
||||
access_mode, const access_data * = NULL);
|
||||
|
||||
#endif /* GCC_BUILTINS_H */
|
||||
|
|
310
gcc/calls.c
310
gcc/calls.c
|
@ -61,7 +61,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "attr-fnspec.h"
|
||||
#include "value-query.h"
|
||||
#include "pointer-query.h"
|
||||
|
||||
#include "gimple-ssa-warn-access.h"
|
||||
#include "tree-pretty-print.h"
|
||||
|
||||
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
|
||||
|
@ -1614,314 +1614,6 @@ get_attr_nonstring_decl (tree expr, tree *ref)
|
|||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Warn about passing a non-string array/pointer to a built-in function
|
||||
that expects a nul-terminated string argument. Returns true if
|
||||
a warning has been issued.*/
|
||||
|
||||
bool
|
||||
maybe_warn_nonstring_arg (tree fndecl, tree exp)
|
||||
{
|
||||
if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
|
||||
return false;
|
||||
|
||||
if (!warn_stringop_overread || warning_suppressed_p (exp, OPT_Wstringop_overread))
|
||||
return false;
|
||||
|
||||
/* Avoid clearly invalid calls (more checking done below). */
|
||||
unsigned nargs = call_expr_nargs (exp);
|
||||
if (!nargs)
|
||||
return false;
|
||||
|
||||
/* The bound argument to a bounded string function like strncpy. */
|
||||
tree bound = NULL_TREE;
|
||||
|
||||
/* The longest known or possible string argument to one of the comparison
|
||||
functions. If the length is less than the bound it is used instead.
|
||||
Since the length is only used for warning and not for code generation
|
||||
disable strict mode in the calls to get_range_strlen below. */
|
||||
tree maxlen = 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. The exception is strncat where the bound may refer to
|
||||
either the destination or the source. */
|
||||
int fncode = DECL_FUNCTION_CODE (fndecl);
|
||||
switch (fncode)
|
||||
{
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_STRNCASECMP:
|
||||
{
|
||||
/* For these, if one argument refers to one or more of a set
|
||||
of string constants or arrays of known size, determine
|
||||
the range of their known or possible lengths and use it
|
||||
conservatively as the bound for the unbounded function,
|
||||
and to adjust the range of the bound of the bounded ones. */
|
||||
for (unsigned argno = 0;
|
||||
argno < MIN (nargs, 2)
|
||||
&& !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++)
|
||||
{
|
||||
tree arg = CALL_EXPR_ARG (exp, argno);
|
||||
if (!get_attr_nonstring_decl (arg))
|
||||
{
|
||||
c_strlen_data lendata = { };
|
||||
/* Set MAXBOUND to an arbitrary non-null non-integer
|
||||
node as a request to have it set to the length of
|
||||
the longest string in a PHI. */
|
||||
lendata.maxbound = arg;
|
||||
get_range_strlen (arg, &lendata, /* eltsize = */ 1);
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BUILT_IN_STRNCAT:
|
||||
case BUILT_IN_STPNCPY:
|
||||
case BUILT_IN_STRNCPY:
|
||||
if (nargs > 2)
|
||||
bound = CALL_EXPR_ARG (exp, 2);
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNDUP:
|
||||
if (nargs > 1)
|
||||
bound = CALL_EXPR_ARG (exp, 1);
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNLEN:
|
||||
{
|
||||
tree arg = CALL_EXPR_ARG (exp, 0);
|
||||
if (!get_attr_nonstring_decl (arg))
|
||||
{
|
||||
c_strlen_data lendata = { };
|
||||
/* Set MAXBOUND to an arbitrary non-null non-integer
|
||||
node as a request to have it set to the length of
|
||||
the longest string in a PHI. */
|
||||
lendata.maxbound = arg;
|
||||
get_range_strlen (arg, &lendata, /* eltsize = */ 1);
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
if (nargs > 1)
|
||||
bound = CALL_EXPR_ARG (exp, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine the range of the bound argument (if specified). */
|
||||
tree bndrng[2] = { NULL_TREE, NULL_TREE };
|
||||
if (bound)
|
||||
{
|
||||
STRIP_NOPS (bound);
|
||||
get_size_range (bound, bndrng);
|
||||
}
|
||||
|
||||
location_t loc = EXPR_LOCATION (exp);
|
||||
|
||||
if (bndrng[0])
|
||||
{
|
||||
/* Diagnose excessive bound prior to the adjustment below and
|
||||
regardless of attribute nonstring. */
|
||||
tree maxobjsize = max_object_size ();
|
||||
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
|
||||
{
|
||||
bool warned = false;
|
||||
if (tree_int_cst_equal (bndrng[0], bndrng[1]))
|
||||
warned = warning_at (loc, OPT_Wstringop_overread,
|
||||
"%qD specified bound %E "
|
||||
"exceeds maximum object size %E",
|
||||
fndecl, bndrng[0], maxobjsize);
|
||||
else
|
||||
warned = warning_at (loc, OPT_Wstringop_overread,
|
||||
"%qD specified bound [%E, %E] "
|
||||
"exceeds maximum object size %E",
|
||||
fndecl, bndrng[0], bndrng[1],
|
||||
maxobjsize);
|
||||
if (warned)
|
||||
suppress_warning (exp, OPT_Wstringop_overread);
|
||||
|
||||
return warned;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxlen && !integer_all_onesp (maxlen))
|
||||
{
|
||||
/* Add one for the nul. */
|
||||
maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen,
|
||||
size_one_node);
|
||||
|
||||
if (!bndrng[0])
|
||||
{
|
||||
/* Conservatively use the upper bound of the lengths for
|
||||
both the lower and the upper bound of the operation. */
|
||||
bndrng[0] = maxlen;
|
||||
bndrng[1] = maxlen;
|
||||
bound = void_type_node;
|
||||
}
|
||||
else if (maxlen)
|
||||
{
|
||||
/* Replace the bound on the operation with the upper bound
|
||||
of the length of the string if the latter is smaller. */
|
||||
if (tree_int_cst_lt (maxlen, bndrng[0]))
|
||||
bndrng[0] = maxlen;
|
||||
else if (tree_int_cst_lt (maxlen, bndrng[1]))
|
||||
bndrng[1] = maxlen;
|
||||
}
|
||||
}
|
||||
|
||||
bool any_arg_warned = false;
|
||||
/* 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))
|
||||
{
|
||||
/* Avoid iterating past the declared argument in a call
|
||||
to function declared without a prototype. */
|
||||
if (argno >= nargs)
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
/* The maximum number of array elements accessed. */
|
||||
offset_int wibnd = 0;
|
||||
|
||||
if (argno && fncode == BUILT_IN_STRNCAT)
|
||||
{
|
||||
/* See if the bound in strncat is derived from the length
|
||||
of the strlen of the destination (as it's expected to be).
|
||||
If so, reset BOUND and FNCODE to trigger a warning. */
|
||||
tree dstarg = CALL_EXPR_ARG (exp, 0);
|
||||
if (is_strlen_related_p (dstarg, bound))
|
||||
{
|
||||
/* The bound applies to the destination, not to the source,
|
||||
so reset these to trigger a warning without mentioning
|
||||
the bound. */
|
||||
bound = NULL;
|
||||
fncode = 0;
|
||||
}
|
||||
else if (bndrng[1])
|
||||
/* Use the upper bound of the range for strncat. */
|
||||
wibnd = wi::to_offset (bndrng[1]);
|
||||
}
|
||||
else if (bndrng[0])
|
||||
/* Use the lower bound of the range for functions other than
|
||||
strncat. */
|
||||
wibnd = wi::to_offset (bndrng[0]);
|
||||
|
||||
/* Determine the size of the argument array if it is one. */
|
||||
offset_int asize = wibnd;
|
||||
bool known_size = false;
|
||||
tree type = TREE_TYPE (decl);
|
||||
|
||||
/* Determine the array size. For arrays of unknown bound and
|
||||
pointers reset BOUND to trigger the appropriate warning. */
|
||||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
{
|
||||
if (tree arrbnd = TYPE_DOMAIN (type))
|
||||
{
|
||||
if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
|
||||
{
|
||||
asize = wi::to_offset (arrbnd) + 1;
|
||||
known_size = true;
|
||||
}
|
||||
}
|
||||
else if (bound == void_type_node)
|
||||
bound = NULL_TREE;
|
||||
}
|
||||
else if (bound == void_type_node)
|
||||
bound = NULL_TREE;
|
||||
|
||||
/* In a call to strncat with a bound in a range whose lower but
|
||||
not upper bound is less than the array size, reset ASIZE to
|
||||
be the same as the bound and the other variable to trigger
|
||||
the apprpriate warning below. */
|
||||
if (fncode == BUILT_IN_STRNCAT
|
||||
&& bndrng[0] != bndrng[1]
|
||||
&& wi::ltu_p (wi::to_offset (bndrng[0]), asize)
|
||||
&& (!known_size
|
||||
|| wi::ltu_p (asize, wibnd)))
|
||||
{
|
||||
asize = wibnd;
|
||||
bound = NULL_TREE;
|
||||
fncode = 0;
|
||||
}
|
||||
|
||||
bool warned = false;
|
||||
|
||||
auto_diagnostic_group d;
|
||||
if (wi::ltu_p (asize, wibnd))
|
||||
{
|
||||
if (bndrng[0] == bndrng[1])
|
||||
warned = warning_at (loc, OPT_Wstringop_overread,
|
||||
"%qD argument %i declared attribute "
|
||||
"%<nonstring%> is smaller than the specified "
|
||||
"bound %wu",
|
||||
fndecl, argno + 1, wibnd.to_uhwi ());
|
||||
else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
|
||||
warned = warning_at (loc, OPT_Wstringop_overread,
|
||||
"%qD argument %i declared attribute "
|
||||
"%<nonstring%> is smaller than "
|
||||
"the specified bound [%E, %E]",
|
||||
fndecl, argno + 1, bndrng[0], bndrng[1]);
|
||||
else
|
||||
warned = warning_at (loc, OPT_Wstringop_overread,
|
||||
"%qD argument %i declared attribute "
|
||||
"%<nonstring%> may be smaller than "
|
||||
"the specified bound [%E, %E]",
|
||||
fndecl, argno + 1, bndrng[0], bndrng[1]);
|
||||
}
|
||||
else if (fncode == BUILT_IN_STRNCAT)
|
||||
; /* Avoid warning for calls to strncat() when the bound
|
||||
is equal to the size of the non-string argument. */
|
||||
else if (!bound)
|
||||
warned = warning_at (loc, OPT_Wstringop_overread,
|
||||
"%qD argument %i declared attribute %<nonstring%>",
|
||||
fndecl, argno + 1);
|
||||
|
||||
if (warned)
|
||||
{
|
||||
inform (DECL_SOURCE_LOCATION (decl),
|
||||
"argument %qD declared here", decl);
|
||||
any_arg_warned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (any_arg_warned)
|
||||
suppress_warning (exp, OPT_Wstringop_overread);
|
||||
|
||||
return any_arg_warned;
|
||||
}
|
||||
|
||||
/* Issue an error if CALL_EXPR was flagged as requiring
|
||||
tall-call optimization. */
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ nowarn_spec_t::nowarn_spec_t (opt_code opt)
|
|||
case OPT_Wformat_overflow_:
|
||||
case OPT_Wformat_truncation_:
|
||||
case OPT_Wrestrict:
|
||||
case OPT_Wsizeof_pointer_memaccess:
|
||||
case OPT_Wstrict_aliasing_:
|
||||
case OPT_Wstringop_overflow_:
|
||||
case OPT_Wstringop_overread:
|
||||
|
|
|
@ -2073,7 +2073,7 @@ gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
|
|||
{
|
||||
/* Avoid folding calls with unterminated arrays. */
|
||||
if (!warning_suppressed_p (stmt, OPT_Wstringop_overread))
|
||||
warn_string_no_nul (loc, NULL_TREE, "strcpy", src, nonstr);
|
||||
warn_string_no_nul (loc, stmt, "strcpy", src, nonstr);
|
||||
suppress_warning (stmt, OPT_Wstringop_overread);
|
||||
return false;
|
||||
}
|
||||
|
@ -3291,7 +3291,7 @@ gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
|
|||
{
|
||||
/* Avoid folding calls with unterminated arrays. */
|
||||
if (!warning_suppressed_p (stmt, OPT_Wstringop_overread))
|
||||
warn_string_no_nul (loc, NULL_TREE, "stpcpy", src, data.decl, size,
|
||||
warn_string_no_nul (loc, stmt, "stpcpy", src, data.decl, size,
|
||||
exact);
|
||||
suppress_warning (stmt, OPT_Wstringop_overread);
|
||||
return false;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,6 +24,9 @@
|
|||
#define GCC_GIMPLE_SSA_WARN_ACCESS_H
|
||||
|
||||
extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
|
||||
extern void warn_string_no_nul (location_t, gimple *, const char *, tree,
|
||||
tree, tree = NULL_TREE, bool = false,
|
||||
const wide_int[2] = NULL);
|
||||
extern void warn_string_no_nul (location_t, tree, const char *, tree,
|
||||
tree, tree = NULL_TREE, bool = false,
|
||||
const wide_int[2] = NULL);
|
||||
|
@ -31,7 +34,17 @@ extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
|
|||
extern void get_size_range (tree, tree[2], const offset_int[2]);
|
||||
|
||||
class access_data;
|
||||
extern bool maybe_warn_for_bound (opt_code, location_t, gimple *, tree,
|
||||
tree[2], tree, const access_data * = NULL);
|
||||
extern bool maybe_warn_for_bound (opt_code, location_t, tree, tree,
|
||||
tree[2], tree, const access_data * = NULL);
|
||||
|
||||
class access_data;
|
||||
extern bool check_access (tree, tree, tree, tree, tree, access_mode,
|
||||
const access_data * = NULL);
|
||||
|
||||
extern bool check_memop_access (tree, tree, tree, tree);
|
||||
extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
|
||||
extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
|
||||
|
||||
#endif // GCC_GIMPLE_SSA_WARN_ACCESS_H
|
||||
|
|
|
@ -203,14 +203,24 @@ public:
|
|||
functions like memcpy. */
|
||||
struct access_data
|
||||
{
|
||||
/* Set the access to at most MAXWRITE and MAXREAD bytes, and
|
||||
at least 1 when MINWRITE or MINREAD, respectively, is set. */
|
||||
access_data (gimple *stmt, access_mode mode,
|
||||
tree maxwrite = NULL_TREE, bool minwrite = false,
|
||||
tree maxread = NULL_TREE, bool minread = false)
|
||||
: stmt (stmt), call (),
|
||||
dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
|
||||
|
||||
/* Set the access to at most MAXWRITE and MAXREAD bytes, and
|
||||
at least 1 when MINWRITE or MINREAD, respectively, is set. */
|
||||
access_data (tree expr, access_mode mode,
|
||||
tree maxwrite = NULL_TREE, bool minwrite = false,
|
||||
tree maxread = NULL_TREE, bool minread = false)
|
||||
: call (expr),
|
||||
: stmt (), call (expr),
|
||||
dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
|
||||
|
||||
/* Access statement. */
|
||||
gimple *stmt;
|
||||
/* Built-in function call. */
|
||||
tree call;
|
||||
/* Destination and source of the access. */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* Test -Wsizeof-pointer-memaccess warnings. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
|
||||
/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat -Wno-stringop-overflow" { target c } } */
|
||||
/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-overread" } */
|
||||
/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat -Wno-stringop-overflow -Wno-stringop-overread" { target c } } */
|
||||
/* { dg-require-effective-target alloca } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
|
|
@ -379,9 +379,9 @@ void test_stnrdup_warn (struct MemArrays *p)
|
|||
T (strndup (p->arr, N));
|
||||
|
||||
|
||||
T (strndup (arr, N + 1)); /* { dg-warning "specified bound 5 exceeds source size 4" } */
|
||||
T (strndup (arr, N + 1)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */
|
||||
T (strndup (parr, N + 1));
|
||||
T (strndup (p->arr, N + 1)); /* { dg-warning "specified bound 5 exceeds source size 4" } */
|
||||
T (strndup (p->arr, N + 1)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */
|
||||
T (strndup (p->parr, N + 1));
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define NOIPA __attribute__ ((noipa))
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern void* memcpy (void*, const void*, size_t);
|
||||
|
@ -19,65 +21,65 @@ const char s1_0[1][0] = { };
|
|||
|
||||
char d[4];
|
||||
|
||||
void* test_memcpy_s0_1 (void *d)
|
||||
NOIPA void* test_memcpy_s0_1 (void *d)
|
||||
{
|
||||
return memcpy (d, s0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_s0_2 (void *d)
|
||||
NOIPA void* test_memcpy_s0_2 (void *d)
|
||||
{
|
||||
return memcpy (d, s0, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_s0_0_1 (void *d)
|
||||
NOIPA void* test_memcpy_s0_0_1 (void *d)
|
||||
{
|
||||
return memcpy (d, s0_0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_s0_0_2 (void *d)
|
||||
NOIPA void* test_memcpy_s0_0_2 (void *d)
|
||||
{
|
||||
return memcpy (d, s0_0, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
|
||||
void* test_memcpy_s0_1_1 (void *d)
|
||||
NOIPA void* test_memcpy_s0_1_1 (void *d)
|
||||
{
|
||||
return memcpy (d, s0_1, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_s0_1_2 (void *d)
|
||||
NOIPA void* test_memcpy_s0_1_2 (void *d)
|
||||
{
|
||||
return memcpy (d, s0_1, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
|
||||
void* test_memcpy_s1_0_1 (void *d)
|
||||
NOIPA void* test_memcpy_s1_0_1 (void *d)
|
||||
{
|
||||
return memcpy (d, s1_0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_s1_0_2 (void *d)
|
||||
NOIPA void* test_memcpy_s1_0_2 (void *d)
|
||||
{
|
||||
return memcpy (d, s1_0, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
|
||||
void* test_memmove_s0_1 (void *d)
|
||||
NOIPA void* test_memmove_s0_1 (void *d)
|
||||
{
|
||||
return memmove (d, s0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memmove_s0_2 (void *d)
|
||||
NOIPA void* test_memmove_s0_2 (void *d)
|
||||
{
|
||||
return memmove (d, s0, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memmove_s0_0_1 (void *d)
|
||||
NOIPA void* test_memmove_s0_0_1 (void *d)
|
||||
{
|
||||
return memmove (d, s0_0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memmove_s0_0_2 (void *d)
|
||||
NOIPA void* test_memmove_s0_0_2 (void *d)
|
||||
{
|
||||
return memmove (d, s0_0, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
@ -90,59 +92,60 @@ const struct Empty e0_0[0][0] = { };
|
|||
const struct Empty e0_1[0][1] = { };
|
||||
const struct Empty e1_0[1][0] = { };
|
||||
|
||||
void* test_memcpy_e_1 (void *d)
|
||||
NOIPA void* test_memcpy_e_1 (void *d)
|
||||
{
|
||||
return memcpy (d, &e, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_e0_1 (void *d)
|
||||
NOIPA void* test_memcpy_e0_1 (void *d)
|
||||
{
|
||||
return memcpy (d, e0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_e0_0_1 (void *d)
|
||||
NOIPA void* test_memcpy_e0_0_1 (void *d)
|
||||
{
|
||||
return memcpy (d, e0_0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_e0_1_1 (void *d)
|
||||
NOIPA void* test_memcpy_e0_1_1 (void *d)
|
||||
{
|
||||
return memcpy (d, e0_1, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
void* test_memcpy_e1_0_1 (void *d)
|
||||
NOIPA void* test_memcpy_e1_0_1 (void *d)
|
||||
{
|
||||
return memcpy (d, e1_0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
|
||||
char* test_strcpy_s0 (char *d)
|
||||
NOIPA char*
|
||||
test_strcpy_s0 (char *d) /* { dg-bogus "-Warray-bounds" "pr101679" { xfail *-*-* } } */
|
||||
{
|
||||
return strcpy (d, s0); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
char* test_strcpy_s0_0 (char *d)
|
||||
NOIPA char* test_strcpy_s0_0 (char *d)
|
||||
{
|
||||
return strcpy (d, s0_0[0]); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
|
||||
char* test_strncpy_s0_1 (char *d)
|
||||
NOIPA char* test_strncpy_s0_1 (char *d)
|
||||
{
|
||||
return strncpy (d, s0, 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
char* test_strncpy_s0_2 (char *d)
|
||||
NOIPA char* test_strncpy_s0_2 (char *d)
|
||||
{
|
||||
return strncpy (d, s0, 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
char* test_strncpy_s0_0_1 (char *d)
|
||||
NOIPA char* test_strncpy_s0_0_1 (char *d)
|
||||
{
|
||||
return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
||||
char* test_strncpy_s0_0_2 (char *d)
|
||||
NOIPA char* test_strncpy_s0_0_2 (char *d)
|
||||
{
|
||||
return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* PR middle-end/95673 - missing -Wstring-compare for an impossible strncmp test
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wstring-compare -ftrack-macro-expansion=0" } */
|
||||
{ dg-options "-O2 -Wall -Wstring-compare -Wno-stringop-overread -ftrack-macro-expansion=0" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
|
|
574
gcc/testsuite/gcc.dg/Wstringop-overread-6.c
Normal file
574
gcc/testsuite/gcc.dg/Wstringop-overread-6.c
Normal file
|
@ -0,0 +1,574 @@
|
|||
/* Verify -Wstringop-overread is issued appropriately for calls to string
|
||||
functions at -O0 and without -Wall.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O0 -ftrack-macro-expansion=0" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
#define S2 "12"
|
||||
#define S9 "123456789"
|
||||
|
||||
// <libint.h> functions.
|
||||
|
||||
char* gettext (const char *);
|
||||
|
||||
// <stdio.h> functions.
|
||||
|
||||
typedef struct FILE FILE;
|
||||
|
||||
int fputs (const char*, FILE*);
|
||||
int fputs_unlocked (const char*, FILE*);
|
||||
|
||||
int puts (const char*);
|
||||
int puts_unlocked (const char*);
|
||||
|
||||
// <string.h> functions.
|
||||
|
||||
void* memchr (const void*, int, size_t);
|
||||
int memcmp (const void*, const void*, size_t);
|
||||
void* memcpy (void*, const void*, size_t);
|
||||
void* mempcpy (void*, const void*, size_t);
|
||||
void* memmove (void*, const void*, size_t);
|
||||
|
||||
char* strchr (const char*, int);
|
||||
char* strrchr (const char*, int);
|
||||
|
||||
int strcmp (const char*, const char*);
|
||||
int strncmp (const char*, const char*, size_t);
|
||||
|
||||
char* strcat (char*, const char*);
|
||||
char* stpcpy (char*, const char*);
|
||||
char* strcpy (char*, const char*);
|
||||
char* stpncpy (char*, const char*, size_t);
|
||||
char* strncpy (char*, const char*, size_t);
|
||||
char* strdup (const char*);
|
||||
char* strndup (const char*, size_t);
|
||||
|
||||
char* strpbrk (const char*, const char*);
|
||||
size_t strcspn (const char*, const char*);
|
||||
size_t strspn (const char*, const char*);
|
||||
char* strstr (const char*, const char*);
|
||||
|
||||
size_t strlen (const char*);
|
||||
size_t strnlen (const char*, size_t);
|
||||
|
||||
|
||||
extern void* malloc (size_t);
|
||||
|
||||
void sink (void*);
|
||||
|
||||
|
||||
extern char *d;
|
||||
extern char a0[0];
|
||||
|
||||
const char arr[7] = "abc\0def";
|
||||
|
||||
/* Unterminated array at the end of ARR above. */
|
||||
#define unterm (arr + __builtin_strlen (arr) + 1)
|
||||
|
||||
/* Size of the unterminated array - 1. */
|
||||
#define unterm_size (sizeof arr - __builtin_strlen (arr) - 1)
|
||||
|
||||
const void* nowarn_memchr (int x)
|
||||
{
|
||||
const char *p1 = unterm;
|
||||
return memchr (p1, x, unterm_size);
|
||||
}
|
||||
|
||||
const void* warn_memchr (int x)
|
||||
{
|
||||
const char *p1 = unterm;
|
||||
return memchr (p1, x, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void* nowarn_memcpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
return memcpy (d, s, unterm_size);
|
||||
}
|
||||
|
||||
void* warn_memcpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
/* Use + 2 for an odd size to prevent the memmove --> MEM_REF transform
|
||||
from defeating the warning (for now). */
|
||||
return memcpy (d, s, unterm_size + 2 | 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void* nowarn_mempcpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
return mempcpy (d, s, unterm_size);
|
||||
}
|
||||
|
||||
void* warn_mempcpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
/* Use + 2 for an odd size to prevent the memmove --> MEM_REF transform
|
||||
from defeating the warning (for now). */
|
||||
return mempcpy (d, s, unterm_size + 2 | 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void* nowarn_memmove (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
return memmove (d, s, unterm_size);
|
||||
}
|
||||
|
||||
void* warn_memmove (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
/* Use + 2 for an odd size to prevent the memmove --> MEM_REF transform
|
||||
from defeating the warning (for now). */
|
||||
return memmove (d, s, unterm_size + 2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
int nowarn_memcmp_1 (const char *p2)
|
||||
{
|
||||
const char *p1 = unterm;
|
||||
return memcmp (p1, p2, unterm_size);
|
||||
}
|
||||
|
||||
int warn_memcmp_1 (const char *p2)
|
||||
{
|
||||
const char *p1 = unterm;
|
||||
return memcmp (p1, p2, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int nowarn_memcmp_2 (const char *p1)
|
||||
{
|
||||
const char *p2 = unterm;
|
||||
return memcmp (p1, p2, unterm_size);
|
||||
}
|
||||
|
||||
int warn_memcmp_2 (const char *p1)
|
||||
{
|
||||
const char *p2 = unterm;
|
||||
return memcmp (p1, p2, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void warn_strcat (void)
|
||||
{
|
||||
strcat (d, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strcat_a0 (void)
|
||||
{
|
||||
strcat (d, a0); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strcat_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
strcat (d, s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_stpcpy (void)
|
||||
{
|
||||
return stpcpy (d, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_stpcpy_a0 (void)
|
||||
{
|
||||
return stpcpy (d, a0); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_stpcpy_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return stpcpy (d, s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_stpcpy_malloc0 (void)
|
||||
{
|
||||
char *s = malloc (0);
|
||||
sink (s);
|
||||
return stpcpy (d, s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void warn_strcpy (void)
|
||||
{
|
||||
strcpy (d, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strcpy_a0 (void)
|
||||
{
|
||||
strcpy (d, a0); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strcpy_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
strcpy (d, s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strcpy_malloc0 (void)
|
||||
{
|
||||
char *s = malloc (0);
|
||||
sink (s);
|
||||
strcpy (d, s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
char* nowarn_stpncpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
return stpncpy (d, s, unterm_size);
|
||||
}
|
||||
|
||||
char* warn_stpncpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
return stpncpy (d, s, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_stpncpy_a0 (void)
|
||||
{
|
||||
return stpncpy (d, a0, 3); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_stpncpy_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return stpncpy (d, s, sizeof arr); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void nowarn_strncpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
strncpy (d, s, unterm_size);
|
||||
}
|
||||
|
||||
void warn_strncpy (void)
|
||||
{
|
||||
const char *s = unterm;
|
||||
strncpy (d, s, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strncpy_a0 (void)
|
||||
{
|
||||
const char *s = a0;
|
||||
strncpy (d, s, sizeof arr); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_strncpy_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
strncpy (d, s, sizeof arr); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
int warn_strlen (void)
|
||||
{
|
||||
return strlen (unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strlen_a0 (void)
|
||||
{
|
||||
return strlen (a0); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strlen_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return strlen (s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strlen_malloc0 (void)
|
||||
{
|
||||
char *s = malloc (0);
|
||||
sink (s);
|
||||
return strlen (s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
int nowarn_strnlen (void)
|
||||
{
|
||||
return strnlen (unterm, unterm_size);
|
||||
}
|
||||
|
||||
int warn_strnlen (void)
|
||||
{
|
||||
return strnlen (unterm, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strnlen_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return strnlen (s, 2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
int warn_strcmp_1 (const char *s)
|
||||
{
|
||||
return strcmp (unterm, s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strcmp_2 (const char *s)
|
||||
{
|
||||
return strcmp (s, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strcmp_2_end (const char *s)
|
||||
{
|
||||
const char *t = arr + sizeof arr;
|
||||
return strcmp (s, t); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
int nowarn_strncmp_1 (const char *s2)
|
||||
{
|
||||
const char *s1 = unterm;
|
||||
return strncmp (s1, s2, unterm_size);
|
||||
}
|
||||
|
||||
int warn_strncmp_1 (const char *s2)
|
||||
{
|
||||
const char *s1 = unterm;
|
||||
return strncmp (s1, s2, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int nowarn_strncmp_2 (const char *s1)
|
||||
{
|
||||
const char *s2 = unterm;
|
||||
return strncmp (s1, s2, unterm_size);
|
||||
}
|
||||
|
||||
int warn_strncmp_2 (const char *s1)
|
||||
{
|
||||
const char *s2 = unterm;
|
||||
return strncmp (s1, s2, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strncmp_2_end (const char *s1)
|
||||
{
|
||||
const char *s2 = arr + sizeof arr;;
|
||||
return strncmp (s1, s2, sizeof arr); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
int nowarn_strncmp_1_s2 (void)
|
||||
{
|
||||
/* Since the read is also bounded by the length of the S2 literal
|
||||
and so safe, expect no warning. */
|
||||
const char *s = unterm;
|
||||
return strncmp (s, S2, unterm_size + 1); // { dg-bogus "-Wstringop-overread" "pr101778" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
int warn_strncmp_2_s2 (void)
|
||||
{
|
||||
/* Same as above. */
|
||||
const char *t = unterm;
|
||||
return strncmp (S2, t, unterm_size + 1); // { dg-bogus "-Wstringop-overread" "pr101778" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
|
||||
int warn_strncmp_1_s9 (void)
|
||||
{
|
||||
/* Since both the bound and the length of the S9 literal are greater
|
||||
than the size of UNNTERM the call reads past the end of the array.
|
||||
Expect a warning. */
|
||||
const char *s1 = unterm;
|
||||
return strncmp (s1, S9, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
int warn_strncmp_2_s9 (void)
|
||||
{
|
||||
/* Same as above. */
|
||||
const char *s2 = unterm;
|
||||
return strncmp (S9, s2, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
const char* warn_strchr (int x)
|
||||
{
|
||||
return strchr (unterm, x); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
const char* warn_strchr_end (int x)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return strchr (s, x); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
const char* warn_strrchr (int x)
|
||||
{
|
||||
return strrchr (unterm, x); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
const char* warn_strrchr_end (int x)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return strrchr (s, x); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
char* warn_strdup (void)
|
||||
{
|
||||
return strdup (unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_strdup_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return strdup (s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
char* nowarn_strndup (void)
|
||||
{
|
||||
return strndup (unterm, unterm_size);
|
||||
}
|
||||
|
||||
char* warn_strndup (void)
|
||||
{
|
||||
return strndup (unterm, unterm_size + 1); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
char* warn_strndup_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return strndup (s, sizeof arr); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
const char* warn_strpbrk_1 (const char *s2)
|
||||
{
|
||||
return strpbrk (unterm, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
const char* warn_strpbrk_2 (const char *s1)
|
||||
{
|
||||
return strpbrk (s1, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
size_t warn_strspn_1 (const char *s2)
|
||||
{
|
||||
return strspn (unterm, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
size_t warn_strspn_1_end (const char *s2)
|
||||
{
|
||||
const char *s1 = arr + sizeof arr;
|
||||
return strspn (s1, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
size_t warn_strspn_2 (const char *s1)
|
||||
{
|
||||
return strspn (s1, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
size_t warn_strspn_2_end (const char *s1)
|
||||
{
|
||||
const char *s2 = arr + sizeof arr;
|
||||
return strspn (s1, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
size_t warn_strcspn_1 (const char *s2)
|
||||
{
|
||||
return strcspn (unterm, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
size_t warn_strcspn_1_end (const char *s2)
|
||||
{
|
||||
const char *s1 = arr + sizeof arr;
|
||||
return strcspn (s1, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
size_t warn_strcspn_2 (const char *s1)
|
||||
{
|
||||
return strcspn (s1, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
size_t warn_strcspn_2_end (const char *s1)
|
||||
{
|
||||
const char *s2 = arr + sizeof arr;
|
||||
return strcspn (s1, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
const char* warn_strstr_1 (const char *s2)
|
||||
{
|
||||
return strstr (unterm, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
const char* warn_strstr_1_end (const char *s2)
|
||||
{
|
||||
const char *s1 = arr + sizeof arr;
|
||||
return strstr (s1, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
const char* warn_strstr_2 (const char *s1)
|
||||
{
|
||||
return strstr (s1, unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
const char* warn_strstr_2_end (const char *s1)
|
||||
{
|
||||
const char *s2 = arr + sizeof arr;
|
||||
return strstr (s1, s2); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void warn_puts (void)
|
||||
{
|
||||
puts (unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_puts_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
puts (s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void warn_fputs (FILE *f)
|
||||
{
|
||||
fputs (unterm, f); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_fputs_end (FILE *f)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
fputs (s, f); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
void warn_puts_unlocked (void)
|
||||
{
|
||||
puts_unlocked (unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_puts_unlocked_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
puts_unlocked (s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
void warn_fputs_unlocked (FILE *f)
|
||||
{
|
||||
fputs_unlocked (unterm, f); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
|
||||
const char* warn_gettext (void)
|
||||
{
|
||||
return gettext (unterm); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
||||
|
||||
const char* warn_gettext_end (void)
|
||||
{
|
||||
const char *s = arr + sizeof arr;
|
||||
return gettext (s); // { dg-warning "-Wstringop-overread" }
|
||||
}
|
|
@ -26,8 +26,8 @@ void test_strnlen_array_cst (void)
|
|||
T (strnlen (ns3, 1));
|
||||
T (strnlen (ns3, 2));
|
||||
T (strnlen (ns3, 3));
|
||||
T (strnlen (ns3, 4)); /* { dg-warning "specified bound 4 exceeds source size 3" } */
|
||||
T (strnlen (ns3, DIFF_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds source size" } */
|
||||
T (strnlen (ns3, 4)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 4|specified bound 4 exceeds source size 3" } */
|
||||
T (strnlen (ns3, DIFF_MAX)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound|specified bound \[0-9\]+ exceeds source size" } */
|
||||
T (strnlen (ns3, SIZE_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
|
||||
|
||||
NONSTRING char ns5[5];
|
||||
|
@ -37,8 +37,8 @@ void test_strnlen_array_cst (void)
|
|||
T (strnlen (ns5, 1));
|
||||
T (strnlen (ns5, 2));
|
||||
T (strnlen (ns5, 3));
|
||||
T (strnlen (ns5, 6)); /* { dg-warning "specified bound 6 exceeds source size 5" } */
|
||||
T (strnlen (ns5, DIFF_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds source size 5" } */
|
||||
T (strnlen (ns5, 6)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 6|specified bound 6 exceeds source size 5" } */
|
||||
T (strnlen (ns5, DIFF_MAX)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound|specified bound \[0-9\]+ exceeds source size 5" } */
|
||||
T (strnlen (ns5, SIZE_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,8 @@ void test_strnlen_array_range (void)
|
|||
T (strnlen (ns3, UR (0, 9)));
|
||||
T (strnlen (ns3, UR (3, 4)));
|
||||
T (strnlen (ns3, UR (3, DIFF_MAX)));
|
||||
T (strnlen (ns3, UR (4, 5))); /* { dg-warning "specified bound \\\[4, 5] exceeds source size 3" } */
|
||||
T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX))); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds source size 3 " } */
|
||||
T (strnlen (ns3, UR (4, 5))); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \\\[4, 5]|specified bound \\\[4, 5] exceeds source size 3" } */
|
||||
T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX))); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \\\[\[0-9\]+, \[0-9\]+] |specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds source size 3 " } */
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,8 +73,8 @@ void test_strnlen_string_cst (void)
|
|||
T (3, "12", 3, 1);
|
||||
T (3, "12", 3, 9);
|
||||
T (3, "123", 3, 1);
|
||||
T (3, "123", 3, 4); /* { dg-warning "specified bound 4 exceeds source size 3" } */
|
||||
T (3, "123", 3, 9); /* { dg-warning "specified bound 9 exceeds source size 3" } */
|
||||
T (3, "123", 3, 4); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 4|specified bound 4 exceeds source size 3" } */
|
||||
T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 9|specified bound 9 exceeds source size 3" } */
|
||||
|
||||
T (5, "1", 2, 1);
|
||||
T (5, "1", 2, 2);
|
||||
|
@ -84,7 +84,7 @@ void test_strnlen_string_cst (void)
|
|||
T (5, "12", 3, 9);
|
||||
T (5, "123", 3, 1);
|
||||
T (5, "123", 3, 5);
|
||||
T (5, "123", 3, 6); /* { dg-warning "specified bound 6 exceeds source size 5" } */
|
||||
T (5, "123", 3, 6); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 6|specified bound 6 exceeds source size 5" } */
|
||||
|
||||
/* Strnlen shouldn't trigger a warning for arrays of unknown size
|
||||
(except for accesses to uninitialized elements when those are
|
||||
|
@ -110,6 +110,6 @@ void test_strnlen_string_range (void)
|
|||
{
|
||||
T (3, "1", 2, UR (0, 1));
|
||||
T (3, "1", 2, UR (3, 9));
|
||||
T (3, "123", 3, UR (4, 5)); /* { dg-warning "specified bound \\\[4, 5] exceeds source size 3" } */
|
||||
T (3, "123", 3, UR (5, 9)); /* { dg-warning "specified bound \\\[5, 9] exceeds source size 3" } */
|
||||
T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \\\[4, 5]|specified bound \\\[4, 5] exceeds source size 3" } */
|
||||
T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \\\[5, 9]|specified bound \\\[5, 9] exceeds source size 3" } */
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ void strnlen_cst (void)
|
|||
T (NS, /* [] */, n);
|
||||
T (NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
|
||||
|
||||
T (NS, 9, n); /* { dg-warning "specified bound \[0-9\]+ exceeds source size 9" } */
|
||||
T (NS, 9, n); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \\d+|specified bound \\d+ exceeds source size 9" } */
|
||||
T (NS, 10, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,6 @@ void strnlen_range (void)
|
|||
T (NS, /* [] */, n);
|
||||
T (NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
|
||||
|
||||
T (NS, 9, n); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds source size 9" } */
|
||||
T (NS, 9, n); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \\\[\\d+, \\d+]|specified bound \\\[\\d+, \\d+] exceeds source size 9" } */
|
||||
T (NS, 10, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
|
||||
}
|
||||
|
|
|
@ -46,10 +46,10 @@ int main(void)
|
|||
int same;
|
||||
|
||||
msg1 = malloc (sizeof (t_s12));
|
||||
msg2 = malloc (sizeof (t_u12));
|
||||
msg2 = malloc (sizeof (t_s12));
|
||||
|
||||
memset (msg1, 0, sizeof (t_s12));
|
||||
memcpy (msg2, &msg1, sizeof (t_s12));
|
||||
memcpy (msg2, msg1, sizeof (t_s12));
|
||||
same = memcmp (msg1, msg2, sizeof (t_s12));
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue