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:
Martin Sebor 2021-08-06 15:29:33 -06:00
parent 629b5699fb
commit 81d6cdd335
16 changed files with 1661 additions and 840 deletions

View file

@ -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)
{

View file

@ -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 */

View file

@ -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. */

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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. */

View file

@ -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;

View file

@ -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));
}

View file

@ -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" } */
}

View file

@ -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;

View 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" }
}

View file

@ -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" } */
}

View file

@ -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\]+" } */
}

View file

@ -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;