Commit b5338fb3 by Martin Sebor Committed by Martin Sebor

PR middle-end/88226 - missing warning on fprintf, fputs, and puts with an unterminated array

gcc/ChangeLog:

	PR middle-end/88226
	* builtins.c (check_nul_terminated_array): New function.
	(fold_builtin_0): Remove declaration.
	(fold_builtin_1): Same.
	(fold_builtin_2): Same.
	(fold_builtin_3): Same.
	(fold_builtin_strpbrk): Add argument.
	(fold_builtin_strspn): Same.
	(fold_builtin_strcspn): Same.
	(expand_builtin_strcat): Call it.  Remove unused argument.
	(expand_builtin_stpncpy): Same.
	(expand_builtin_strncat): Same.
	(expand_builtin_strncpy): Same.  Adjust indentation.
	(expand_builtin_strcmp): Same.
	(expand_builtin_strncmp): Same.
	(expand_builtin_fork_or_exec): Same.
	(expand_builtin): Handle more built-ins.
	(fold_builtin_2): Add argument.
	(fold_builtin_n): Make static.  Add argument.
	(fold_call_expr): Pass new argument to fold_builtin_n and fold_builtin_2.
	(fold_builtin_call_array): Pass new argument to fold_builtin_n.
	(fold_builtin_strpbrk): Add argument.  Call check_nul_terminated_array.
	(fold_call_stmt): Pass new argument to fold_builtin_n.
	* builtins.h: Correct a comment.
	* gimple-fold.c (gimple_fold_builtin_strchr): Call
	check_nul_terminated_array.
	* tree-ssa-strlen.c (handle_builtin_strlen): Call
	check_nul_terminated_array.
	(handle_builtin_strchr): Same.
	(handle_builtin_string_cmp): Same.

gcc/testsuite/ChangeLog:

	PR middle-end/88226
	* gcc.dg/Wstringop-overflow-22.c: New test.
	* gcc.dg/tree-ssa/builtin-fprintf-warn-1.c: Remove xfails.

From-SVN: r278623
parent 86b0eb81
...@@ -8,6 +8,39 @@ ...@@ -8,6 +8,39 @@
2019-11-22 Martin Sebor <msebor@redhat.com> 2019-11-22 Martin Sebor <msebor@redhat.com>
PR middle-end/88226
* builtins.c (check_nul_terminated_array): New function.
(fold_builtin_0): Remove declaration.
(fold_builtin_1): Same.
(fold_builtin_2): Same.
(fold_builtin_3): Same.
(fold_builtin_strpbrk): Add argument.
(fold_builtin_strspn): Same.
(fold_builtin_strcspn): Same.
(expand_builtin_strcat): Call it. Remove unused argument.
(expand_builtin_stpncpy): Same.
(expand_builtin_strncat): Same.
(expand_builtin_strncpy): Same. Adjust indentation.
(expand_builtin_strcmp): Same.
(expand_builtin_strncmp): Same.
(expand_builtin_fork_or_exec): Same.
(expand_builtin): Handle more built-ins.
(fold_builtin_2): Add argument.
(fold_builtin_n): Make static. Add argument.
(fold_call_expr): Pass new argument to fold_builtin_n and fold_builtin_2.
(fold_builtin_call_array): Pass new argument to fold_builtin_n.
(fold_builtin_strpbrk): Add argument. Call check_nul_terminated_array.
(fold_call_stmt): Pass new argument to fold_builtin_n.
* builtins.h: Correct a comment.
* gimple-fold.c (gimple_fold_builtin_strchr): Call
check_nul_terminated_array.
* tree-ssa-strlen.c (handle_builtin_strlen): Call
check_nul_terminated_array.
(handle_builtin_strchr): Same.
(handle_builtin_string_cmp): Same.
2019-11-22 Martin Sebor <msebor@redhat.com>
PR tree-optimization/92501 PR tree-optimization/92501
* gimple-fold.c ((gimple_fold_builtin_string_compare): Let strncmp * gimple-fold.c ((gimple_fold_builtin_string_compare): Let strncmp
handle unterminated arrays. Rename local variables for clarity. handle unterminated arrays. Rename local variables for clarity.
...@@ -131,7 +131,7 @@ static rtx expand_builtin_memory_copy_args (tree dest, tree src, tree len, ...@@ -131,7 +131,7 @@ static rtx expand_builtin_memory_copy_args (tree dest, tree src, tree len,
static rtx expand_builtin_memmove (tree, rtx); static rtx expand_builtin_memmove (tree, rtx);
static rtx expand_builtin_mempcpy (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_mempcpy_args (tree, tree, tree, rtx, tree, memop_ret);
static rtx expand_builtin_strcat (tree, rtx); static rtx expand_builtin_strcat (tree);
static rtx expand_builtin_strcpy (tree, rtx); static rtx expand_builtin_strcpy (tree, rtx);
static rtx expand_builtin_strcpy_args (tree, tree, 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_stpcpy (tree, rtx, machine_mode);
...@@ -166,15 +166,11 @@ static tree fold_builtin_fabs (location_t, tree, tree); ...@@ -166,15 +166,11 @@ static tree fold_builtin_fabs (location_t, tree, tree);
static tree fold_builtin_abs (location_t, tree, tree); static tree fold_builtin_abs (location_t, tree, tree);
static tree fold_builtin_unordered_cmp (location_t, tree, tree, tree, enum tree_code, static tree fold_builtin_unordered_cmp (location_t, tree, tree, tree, enum tree_code,
enum tree_code); enum tree_code);
static tree fold_builtin_0 (location_t, tree);
static tree fold_builtin_1 (location_t, tree, tree);
static tree fold_builtin_2 (location_t, tree, tree, tree);
static tree fold_builtin_3 (location_t, tree, tree, tree, tree);
static tree fold_builtin_varargs (location_t, tree, tree*, int); static tree fold_builtin_varargs (location_t, tree, tree*, int);
static tree fold_builtin_strpbrk (location_t, tree, tree, tree); static tree fold_builtin_strpbrk (location_t, tree, tree, tree, tree);
static tree fold_builtin_strspn (location_t, tree, tree); static tree fold_builtin_strspn (location_t, tree, tree, tree);
static tree fold_builtin_strcspn (location_t, tree, tree); static tree fold_builtin_strcspn (location_t, tree, tree, tree);
static rtx expand_builtin_object_size (tree); static rtx expand_builtin_object_size (tree);
static rtx expand_builtin_memory_chk (tree, rtx, machine_mode, static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
...@@ -564,6 +560,51 @@ warn_string_no_nul (location_t loc, const char *fn, tree arg, tree decl) ...@@ -564,6 +560,51 @@ warn_string_no_nul (location_t loc, const char *fn, tree arg, tree decl)
} }
} }
/* For a call EXPR (which may be null) that expects a string argument
and SRC as the argument, returns false if SRC is a character array
with no terminating NUL. When nonnull, BOUND is the number of
characters in which to expect the terminating NUL.
When EXPR is nonnull also issues a warning. */
bool
check_nul_terminated_array (tree expr, tree src, tree bound /* = NULL_TREE */)
{
tree size;
bool exact;
tree nonstr = unterminated_array (src, &size, &exact);
if (!nonstr)
return true;
/* NONSTR refers to the non-nul terminated constant array and SIZE
is the constant size of the array in bytes. EXACT is true when
SIZE is exact. */
if (bound)
{
wide_int min, max;
if (TREE_CODE (bound) == INTEGER_CST)
min = max = wi::to_wide (bound);
else
{
value_range_kind rng = get_range_info (bound, &min, &max);
if (rng != VR_RANGE)
return true;
}
if (wi::leu_p (min, wi::to_wide (size)))
return true;
}
if (expr && !TREE_NO_WARNING (expr))
{
tree fndecl = get_callee_fndecl (expr);
const char *fname = IDENTIFIER_POINTER (DECL_NAME (fndecl));
warn_string_no_nul (EXPR_LOCATION (expr), fname, src, nonstr);
}
return false;
}
/* If EXP refers to an unterminated constant character array return /* If EXP refers to an unterminated constant character array return
the declaration of the object of which the array is a member or the declaration of the object of which the array is a member or
element and if SIZE is not null, set *SIZE to the size of element and if SIZE is not null, set *SIZE to the size of
...@@ -4024,7 +4065,7 @@ expand_movstr (tree dest, tree src, rtx target, memop_ret retmode) ...@@ -4024,7 +4065,7 @@ expand_movstr (tree dest, tree src, rtx target, memop_ret retmode)
to the library function. */ to the library function. */
static rtx static rtx
expand_builtin_strcat (tree exp, rtx) expand_builtin_strcat (tree exp)
{ {
if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE) if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
|| !warn_stringop_overflow) || !warn_stringop_overflow)
...@@ -4033,6 +4074,10 @@ expand_builtin_strcat (tree exp, rtx) ...@@ -4033,6 +4074,10 @@ expand_builtin_strcat (tree exp, rtx)
tree dest = CALL_EXPR_ARG (exp, 0); tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1); tree src = CALL_EXPR_ARG (exp, 1);
/* Detect unterminated source (only). */
if (!check_nul_terminated_array (exp, src))
return NULL_RTX;
/* There is no way here to determine the length of the string in /* There is no way here to determine the length of the string in
the destination to which the SRC string is being appended so the destination to which the SRC string is being appended so
just diagnose cases when the souce string is longer than just diagnose cases when the souce string is longer than
...@@ -4228,6 +4273,8 @@ expand_builtin_stpncpy (tree exp, rtx) ...@@ -4228,6 +4273,8 @@ expand_builtin_stpncpy (tree exp, rtx)
/* The exact number of bytes to write (not the maximum). */ /* The exact number of bytes to write (not the maximum). */
tree len = CALL_EXPR_ARG (exp, 2); tree len = CALL_EXPR_ARG (exp, 2);
if (!check_nul_terminated_array (exp, src, len))
return NULL_RTX;
/* The size of the destination object. */ /* The size of the destination object. */
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
...@@ -4330,6 +4377,11 @@ expand_builtin_strncat (tree exp, rtx) ...@@ -4330,6 +4377,11 @@ expand_builtin_strncat (tree exp, rtx)
tree src = CALL_EXPR_ARG (exp, 1); tree src = CALL_EXPR_ARG (exp, 1);
/* The upper bound on the number of bytes to write. */ /* The upper bound on the number of bytes to write. */
tree maxread = CALL_EXPR_ARG (exp, 2); 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. */ /* The length of the source sequence. */
tree slen = c_strlen (src, 1); tree slen = c_strlen (src, 1);
...@@ -4391,59 +4443,63 @@ expand_builtin_strncpy (tree exp, rtx target) ...@@ -4391,59 +4443,63 @@ expand_builtin_strncpy (tree exp, rtx target)
{ {
location_t loc = EXPR_LOCATION (exp); location_t loc = EXPR_LOCATION (exp);
if (validate_arglist (exp, if (!validate_arglist (exp,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{ return NULL_RTX;
tree dest = CALL_EXPR_ARG (exp, 0); tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1); tree src = CALL_EXPR_ARG (exp, 1);
/* The number of bytes to write (not the maximum). */ /* The number of bytes to write (not the maximum). */
tree len = CALL_EXPR_ARG (exp, 2); tree len = CALL_EXPR_ARG (exp, 2);
/* The length of the source sequence. */
tree slen = c_strlen (src, 1);
if (warn_stringop_overflow) if (!check_nul_terminated_array (exp, src, len))
{ return NULL_RTX;
tree destsize = compute_objsize (dest,
warn_stringop_overflow - 1);
/* The number of bytes to write is LEN but check_access will also /* The length of the source sequence. */
check SLEN if LEN's value isn't known. */ tree slen = c_strlen (src, 1);
check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
destsize);
}
/* We must be passed a constant len and src parameter. */ if (warn_stringop_overflow)
if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen)) {
return NULL_RTX; tree destsize = compute_objsize (dest,
warn_stringop_overflow - 1);
slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1)); /* 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, dest, src, len, /*maxread=*/NULL_TREE, src,
destsize);
}
/* We're required to pad with trailing zeros if the requested /* We must be passed a constant len and src parameter. */
len is greater than strlen(s2)+1. In that case try to if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
use store_by_pieces, if it fails, punt. */ return NULL_RTX;
if (tree_int_cst_lt (slen, len))
{
unsigned int dest_align = get_pointer_alignment (dest);
const char *p = c_getstr (src);
rtx dest_mem;
if (!p || dest_align == 0 || !tree_fits_uhwi_p (len)
|| !can_store_by_pieces (tree_to_uhwi (len),
builtin_strncpy_read_str,
CONST_CAST (char *, p),
dest_align, false))
return NULL_RTX;
dest_mem = get_memory_rtx (dest, len); slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
store_by_pieces (dest_mem, tree_to_uhwi (len),
builtin_strncpy_read_str, /* We're required to pad with trailing zeros if the requested
CONST_CAST (char *, p), dest_align, false, len is greater than strlen(s2)+1. In that case try to
RETURN_BEGIN); use store_by_pieces, if it fails, punt. */
dest_mem = force_operand (XEXP (dest_mem, 0), target); if (tree_int_cst_lt (slen, len))
dest_mem = convert_memory_address (ptr_mode, dest_mem); {
return dest_mem; unsigned int dest_align = get_pointer_alignment (dest);
} const char *p = c_getstr (src);
rtx dest_mem;
if (!p || dest_align == 0 || !tree_fits_uhwi_p (len)
|| !can_store_by_pieces (tree_to_uhwi (len),
builtin_strncpy_read_str,
CONST_CAST (char *, p),
dest_align, false))
return NULL_RTX;
dest_mem = get_memory_rtx (dest, len);
store_by_pieces (dest_mem, tree_to_uhwi (len),
builtin_strncpy_read_str,
CONST_CAST (char *, p), dest_align, false,
RETURN_BEGIN);
dest_mem = force_operand (XEXP (dest_mem, 0), target);
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
} }
return NULL_RTX; return NULL_RTX;
} }
...@@ -4822,6 +4878,13 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) ...@@ -4822,6 +4878,13 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return NULL_RTX; return NULL_RTX;
tree arg1 = CALL_EXPR_ARG (exp, 0);
tree arg2 = CALL_EXPR_ARG (exp, 1);
if (!check_nul_terminated_array (exp, arg1)
|| !check_nul_terminated_array (exp, arg2))
return NULL_RTX;
/* Due to the performance benefit, always inline the calls first. */ /* Due to the performance benefit, always inline the calls first. */
rtx result = NULL_RTX; rtx result = NULL_RTX;
result = inline_expand_builtin_string_cmp (exp, target); result = inline_expand_builtin_string_cmp (exp, target);
...@@ -4833,9 +4896,6 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) ...@@ -4833,9 +4896,6 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
if (cmpstr_icode == CODE_FOR_nothing && cmpstrn_icode == CODE_FOR_nothing) if (cmpstr_icode == CODE_FOR_nothing && cmpstrn_icode == CODE_FOR_nothing)
return NULL_RTX; return NULL_RTX;
tree arg1 = CALL_EXPR_ARG (exp, 0);
tree arg2 = CALL_EXPR_ARG (exp, 1);
unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
...@@ -4941,6 +5001,14 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target, ...@@ -4941,6 +5001,14 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX; return NULL_RTX;
tree arg1 = CALL_EXPR_ARG (exp, 0);
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;
/* Due to the performance benefit, always inline the calls first. */ /* Due to the performance benefit, always inline the calls first. */
rtx result = NULL_RTX; rtx result = NULL_RTX;
result = inline_expand_builtin_string_cmp (exp, target); result = inline_expand_builtin_string_cmp (exp, target);
...@@ -4956,10 +5024,6 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target, ...@@ -4956,10 +5024,6 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
tree len; tree len;
tree arg1 = CALL_EXPR_ARG (exp, 0);
tree arg2 = CALL_EXPR_ARG (exp, 1);
tree arg3 = CALL_EXPR_ARG (exp, 2);
unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
...@@ -5954,6 +6018,26 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore) ...@@ -5954,6 +6018,26 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
tree id, decl; tree id, decl;
tree call; tree call;
if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
{
/* Detect unterminated path. */
if (!check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0)))
return NULL_RTX;
/* Also detect unterminated first argument. */
switch (DECL_FUNCTION_CODE (fn))
{
case BUILT_IN_EXECL:
case BUILT_IN_EXECLE:
case BUILT_IN_EXECLP:
if (!check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0)))
return NULL_RTX;
default:
break;
}
}
/* If we are not profiling, just call the function. */ /* If we are not profiling, just call the function. */
if (!profile_arc_flag) if (!profile_arc_flag)
return NULL_RTX; return NULL_RTX;
...@@ -7604,11 +7688,49 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, ...@@ -7604,11 +7688,49 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
break; break;
case BUILT_IN_STRCAT: case BUILT_IN_STRCAT:
target = expand_builtin_strcat (exp, target); target = expand_builtin_strcat (exp);
if (target) if (target)
return target; return target;
break; 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_nul_terminated_array (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_nul_terminated_array (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_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0));
break;
case BUILT_IN_STRNDUP:
if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
check_nul_terminated_array (exp,
CALL_EXPR_ARG (exp, 0),
CALL_EXPR_ARG (exp, 1));
break;
case BUILT_IN_STRCASECMP:
case BUILT_IN_STRSTR:
if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{
check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0));
check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 1));
}
break;
case BUILT_IN_STRCPY: case BUILT_IN_STRCPY:
target = expand_builtin_strcpy (exp, target); target = expand_builtin_strcpy (exp, target);
if (target) if (target)
...@@ -9671,11 +9793,12 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0) ...@@ -9671,11 +9793,12 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
} }
/* Fold a call to built-in function FNDECL with 2 arguments, ARG0 and ARG1. /* Folds a call EXPR (which may be null) to built-in function FNDECL
This function returns NULL_TREE if no simplification was possible. */ with 2 arguments, ARG0 and ARG1. This function returns NULL_TREE
if no simplification was possible. */
static tree static tree
fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1) fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
{ {
tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree type = TREE_TYPE (TREE_TYPE (fndecl));
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
...@@ -9703,13 +9826,13 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1) ...@@ -9703,13 +9826,13 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
return fold_builtin_modf (loc, arg0, arg1, type); return fold_builtin_modf (loc, arg0, arg1, type);
case BUILT_IN_STRSPN: case BUILT_IN_STRSPN:
return fold_builtin_strspn (loc, arg0, arg1); return fold_builtin_strspn (loc, expr, arg0, arg1);
case BUILT_IN_STRCSPN: case BUILT_IN_STRCSPN:
return fold_builtin_strcspn (loc, arg0, arg1); return fold_builtin_strcspn (loc, expr, arg0, arg1);
case BUILT_IN_STRPBRK: case BUILT_IN_STRPBRK:
return fold_builtin_strpbrk (loc, arg0, arg1, type); return fold_builtin_strpbrk (loc, expr, arg0, arg1, type);
case BUILT_IN_EXPECT: case BUILT_IN_EXPECT:
return fold_builtin_expect (loc, arg0, arg1, NULL_TREE, NULL_TREE); return fold_builtin_expect (loc, arg0, arg1, NULL_TREE, NULL_TREE);
...@@ -9827,13 +9950,14 @@ fold_builtin_3 (location_t loc, tree fndecl, ...@@ -9827,13 +9950,14 @@ fold_builtin_3 (location_t loc, tree fndecl,
return NULL_TREE; return NULL_TREE;
} }
/* Fold a call to built-in function FNDECL. ARGS is an array of NARGS /* Folds a call EXPR (which may be null) to built-in function FNDECL.
arguments. IGNORE is true if the result of the ARGS is an array of NARGS arguments. IGNORE is true if the result
function call is ignored. This function returns NULL_TREE if no of the function call is ignored. This function returns NULL_TREE
simplification was possible. */ if no simplification was possible. */
tree static tree
fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool) fold_builtin_n (location_t loc, tree expr, tree fndecl, tree *args,
int nargs, bool)
{ {
tree ret = NULL_TREE; tree ret = NULL_TREE;
...@@ -9846,7 +9970,7 @@ fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool) ...@@ -9846,7 +9970,7 @@ fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool)
ret = fold_builtin_1 (loc, fndecl, args[0]); ret = fold_builtin_1 (loc, fndecl, args[0]);
break; break;
case 2: case 2:
ret = fold_builtin_2 (loc, fndecl, args[0], args[1]); ret = fold_builtin_2 (loc, expr, fndecl, args[0], args[1]);
break; break;
case 3: case 3:
ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]); ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]);
...@@ -9944,7 +10068,7 @@ fold_call_expr (location_t loc, tree exp, bool ignore) ...@@ -9944,7 +10068,7 @@ fold_call_expr (location_t loc, tree exp, bool ignore)
else else
{ {
tree *args = CALL_EXPR_ARGP (exp); tree *args = CALL_EXPR_ARGP (exp);
ret = fold_builtin_n (loc, fndecl, args, nargs, ignore); ret = fold_builtin_n (loc, exp, fndecl, args, nargs, ignore);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -9982,7 +10106,7 @@ fold_builtin_call_array (location_t loc, tree, ...@@ -9982,7 +10106,7 @@ fold_builtin_call_array (location_t loc, tree,
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.fold_builtin (fndecl, n, argarray, false); return targetm.fold_builtin (fndecl, n, argarray, false);
else else
return fold_builtin_n (loc, fndecl, argarray, n, false); return fold_builtin_n (loc, NULL_TREE, fndecl, argarray, n, false);
} }
return NULL_TREE; return NULL_TREE;
...@@ -10134,51 +10258,53 @@ readonly_data_expr (tree exp) ...@@ -10134,51 +10258,53 @@ readonly_data_expr (tree exp)
form of the builtin function call. */ form of the builtin function call. */
static tree static tree
fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type) fold_builtin_strpbrk (location_t loc, tree expr, tree s1, tree s2, tree type)
{ {
if (!validate_arg (s1, POINTER_TYPE) if (!validate_arg (s1, POINTER_TYPE)
|| !validate_arg (s2, POINTER_TYPE)) || !validate_arg (s2, POINTER_TYPE))
return NULL_TREE; return NULL_TREE;
else
{
tree fn;
const char *p1, *p2;
p2 = c_getstr (s2); if (!check_nul_terminated_array (expr, s1)
if (p2 == NULL) || !check_nul_terminated_array (expr, s2))
return NULL_TREE; return NULL_TREE;
p1 = c_getstr (s1); tree fn;
if (p1 != NULL) const char *p1, *p2;
{
const char *r = strpbrk (p1, p2);
tree tem;
if (r == NULL) p2 = c_getstr (s2);
return build_int_cst (TREE_TYPE (s1), 0); if (p2 == NULL)
return NULL_TREE;
/* Return an offset into the constant string argument. */ p1 = c_getstr (s1);
tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); if (p1 != NULL)
return fold_convert_loc (loc, type, tem); {
} const char *r = strpbrk (p1, p2);
tree tem;
if (p2[0] == '\0') if (r == NULL)
/* strpbrk(x, "") == NULL. return build_int_cst (TREE_TYPE (s1), 0);
Evaluate and ignore s1 in case it had side-effects. */
return omit_one_operand_loc (loc, type, integer_zero_node, s1);
if (p2[1] != '\0') /* Return an offset into the constant string argument. */
return NULL_TREE; /* Really call strpbrk. */ tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
return fold_convert_loc (loc, type, tem);
}
fn = builtin_decl_implicit (BUILT_IN_STRCHR); if (p2[0] == '\0')
if (!fn) /* strpbrk(x, "") == NULL.
return NULL_TREE; Evaluate and ignore s1 in case it had side-effects. */
return omit_one_operand_loc (loc, type, integer_zero_node, s1);
/* New argument list transforming strpbrk(s1, s2) to if (p2[1] != '\0')
strchr(s1, s2[0]). */ return NULL_TREE; /* Really call strpbrk. */
return build_call_expr_loc (loc, fn, 2, s1,
build_int_cst (integer_type_node, p2[0])); fn = builtin_decl_implicit (BUILT_IN_STRCHR);
} if (!fn)
return NULL_TREE;
/* New argument list transforming strpbrk(s1, s2) to
strchr(s1, s2[0]). */
return build_call_expr_loc (loc, fn, 2, s1,
build_int_cst (integer_type_node, p2[0]));
} }
/* Simplify a call to the strspn builtin. S1 and S2 are the arguments /* Simplify a call to the strspn builtin. S1 and S2 are the arguments
...@@ -10200,23 +10326,25 @@ fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type) ...@@ -10200,23 +10326,25 @@ fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type)
form of the builtin function call. */ form of the builtin function call. */
static tree static tree
fold_builtin_strspn (location_t loc, tree s1, tree s2) fold_builtin_strspn (location_t loc, tree expr, tree s1, tree s2)
{ {
if (!validate_arg (s1, POINTER_TYPE) if (!validate_arg (s1, POINTER_TYPE)
|| !validate_arg (s2, POINTER_TYPE)) || !validate_arg (s2, POINTER_TYPE))
return NULL_TREE; return NULL_TREE;
else
{
const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
/* If either argument is "", return NULL_TREE. */ if (!check_nul_terminated_array (expr, s1)
if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')) || !check_nul_terminated_array (expr, s2))
/* Evaluate and ignore both arguments in case either one has return NULL_TREE;
side-effects. */
return omit_two_operands_loc (loc, size_type_node, size_zero_node, const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
/* If either argument is "", return NULL_TREE. */
if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
/* Evaluate and ignore both arguments in case either one has
side-effects. */
return omit_two_operands_loc (loc, size_type_node, size_zero_node,
s1, s2); s1, s2);
return NULL_TREE; return NULL_TREE;
}
} }
/* Simplify a call to the strcspn builtin. S1 and S2 are the arguments /* Simplify a call to the strcspn builtin. S1 and S2 are the arguments
...@@ -10238,38 +10366,40 @@ fold_builtin_strspn (location_t loc, tree s1, tree s2) ...@@ -10238,38 +10366,40 @@ fold_builtin_strspn (location_t loc, tree s1, tree s2)
form of the builtin function call. */ form of the builtin function call. */
static tree static tree
fold_builtin_strcspn (location_t loc, tree s1, tree s2) fold_builtin_strcspn (location_t loc, tree expr, tree s1, tree s2)
{ {
if (!validate_arg (s1, POINTER_TYPE) if (!validate_arg (s1, POINTER_TYPE)
|| !validate_arg (s2, POINTER_TYPE)) || !validate_arg (s2, POINTER_TYPE))
return NULL_TREE; return NULL_TREE;
else
if (!check_nul_terminated_array (expr, s1)
|| !check_nul_terminated_array (expr, s2))
return NULL_TREE;
/* If the first argument is "", return NULL_TREE. */
const char *p1 = c_getstr (s1);
if (p1 && *p1 == '\0')
{ {
/* If the first argument is "", return NULL_TREE. */ /* Evaluate and ignore argument s2 in case it has
const char *p1 = c_getstr (s1); side-effects. */
if (p1 && *p1 == '\0') return omit_one_operand_loc (loc, size_type_node,
{
/* Evaluate and ignore argument s2 in case it has
side-effects. */
return omit_one_operand_loc (loc, size_type_node,
size_zero_node, s2); size_zero_node, s2);
} }
/* If the second argument is "", return __builtin_strlen(s1). */ /* If the second argument is "", return __builtin_strlen(s1). */
const char *p2 = c_getstr (s2); const char *p2 = c_getstr (s2);
if (p2 && *p2 == '\0') if (p2 && *p2 == '\0')
{ {
tree fn = builtin_decl_implicit (BUILT_IN_STRLEN); tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
/* If the replacement _DECL isn't initialized, don't do the /* If the replacement _DECL isn't initialized, don't do the
transformation. */ transformation. */
if (!fn) if (!fn)
return NULL_TREE; return NULL_TREE;
return build_call_expr_loc (loc, fn, 1, s1); return build_call_expr_loc (loc, fn, 1, s1);
}
return NULL_TREE;
} }
return NULL_TREE;
} }
/* Fold the next_arg or va_start call EXP. Returns true if there was an error /* Fold the next_arg or va_start call EXP. Returns true if there was an error
...@@ -11112,7 +11242,7 @@ fold_call_stmt (gcall *stmt, bool ignore) ...@@ -11112,7 +11242,7 @@ fold_call_stmt (gcall *stmt, bool ignore)
} }
else else
{ {
ret = fold_builtin_n (loc, fndecl, args, nargs, ignore); ret = fold_builtin_n (loc, NULL_TREE, fndecl, args, nargs, ignore);
if (ret) if (ret)
{ {
/* Propagate location information from original call to /* Propagate location information from original call to
......
...@@ -91,7 +91,7 @@ struct c_strlen_data ...@@ -91,7 +91,7 @@ struct c_strlen_data
tree minlen; tree minlen;
tree maxlen; tree maxlen;
tree maxbound; tree maxbound;
/* When non-null, NONSTR refers to the declaration known to store /* When non-null, DECL refers to the declaration known to store
an unterminated constant character array, as in: an unterminated constant character array, as in:
const char s[] = { 'a', 'b', 'c' }; const char s[] = { 'a', 'b', 'c' };
It is used to diagnose uses of such arrays in functions such as It is used to diagnose uses of such arrays in functions such as
...@@ -125,7 +125,6 @@ extern tree fold_builtin_expect (location_t, tree, tree, tree, tree); ...@@ -125,7 +125,6 @@ extern tree fold_builtin_expect (location_t, tree, tree, tree, tree);
extern bool avoid_folding_inline_builtin (tree); extern bool avoid_folding_inline_builtin (tree);
extern tree fold_call_expr (location_t, tree, bool); extern tree fold_call_expr (location_t, tree, bool);
extern tree fold_builtin_call_array (location_t, tree, tree, int, tree *); extern tree fold_builtin_call_array (location_t, tree, tree, int, tree *);
extern tree fold_builtin_n (location_t, tree, tree *, int, bool);
extern bool validate_gimple_arglist (const gcall *, ...); extern bool validate_gimple_arglist (const gcall *, ...);
extern rtx default_expand_builtin (tree, rtx, rtx, machine_mode, int); extern rtx default_expand_builtin (tree, rtx, rtx, machine_mode, int);
extern bool fold_builtin_next_arg (tree, bool); extern bool fold_builtin_next_arg (tree, bool);
...@@ -148,6 +147,7 @@ extern bool target_char_cst_p (tree t, char *p); ...@@ -148,6 +147,7 @@ extern bool target_char_cst_p (tree t, char *p);
extern internal_fn associated_internal_fn (tree); extern internal_fn associated_internal_fn (tree);
extern internal_fn replacement_internal_fn (gcall *); extern internal_fn replacement_internal_fn (gcall *);
bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
extern void warn_string_no_nul (location_t, const char *, tree, tree); extern void warn_string_no_nul (location_t, const char *, tree, tree);
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL); extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree); extern bool builtin_with_linkage_p (tree);
......
...@@ -1899,6 +1899,11 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr) ...@@ -1899,6 +1899,11 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
if (!gimple_call_lhs (stmt)) if (!gimple_call_lhs (stmt))
return false; return false;
/* Avoid folding if the first argument is not a nul-terminated array.
Defer warning until later. */
if (!check_nul_terminated_array (NULL_TREE, str))
return false;
if ((p = c_getstr (str)) && target_char_cst_p (c, &ch)) if ((p = c_getstr (str)) && target_char_cst_p (c, &ch))
{ {
const char *p1 = is_strrchr ? strrchr (p, ch) : strchr (p, ch); const char *p1 = is_strrchr ? strrchr (p, ch) : strchr (p, ch);
...@@ -1973,18 +1978,23 @@ static bool ...@@ -1973,18 +1978,23 @@ static bool
gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi) gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi)
{ {
gimple *stmt = gsi_stmt (*gsi); gimple *stmt = gsi_stmt (*gsi);
if (!gimple_call_lhs (stmt))
return false;
tree haystack = gimple_call_arg (stmt, 0); tree haystack = gimple_call_arg (stmt, 0);
tree needle = gimple_call_arg (stmt, 1); tree needle = gimple_call_arg (stmt, 1);
const char *p, *q;
if (!gimple_call_lhs (stmt)) /* Avoid folding if either argument is not a nul-terminated array.
Defer warning until later. */
if (!check_nul_terminated_array (NULL_TREE, haystack)
|| !check_nul_terminated_array (NULL_TREE, needle))
return false; return false;
q = c_getstr (needle); const char *q = c_getstr (needle);
if (q == NULL) if (q == NULL)
return false; return false;
if ((p = c_getstr (haystack))) if (const char *p = c_getstr (haystack))
{ {
const char *r = strstr (p, q); const char *r = strstr (p, q);
......
2019-11-22 Martin Sebor <msebor@redhat.com> 2019-11-22 Martin Sebor <msebor@redhat.com>
PR middle-end/88226
* gcc.dg/Wstringop-overflow-22.c: New test.
* gcc.dg/tree-ssa/builtin-fprintf-warn-1.c: Remove xfails.
2019-11-22 Martin Sebor <msebor@redhat.com>
PR tree-optimization/92501 PR tree-optimization/92501
* gcc.dg/strcmpopt_7.c: New test. * gcc.dg/strcmpopt_7.c: New test.
......
/* { dg-do compile }
{ dg-options "-O2 -Wall -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
#define NULL (void*)0
const char a[] = { 'a', 'b', 'c', 'd' };
const char b[] = { 'a', '\0', 'c', '\0', 'e' };
#define CONCAT(a, b) a ## b
#define CAT(a, b) CONCAT (a, b)
typedef struct FILE FILE;
extern FILE *fp;
extern char *d;
extern const char *s;
extern int n;
#define T(func, ...) \
__attribute__ ((noipa)) void \
CAT (test_ ## func, __LINE__) (void) \
{ \
sink (0, __builtin_ ## func (__VA_ARGS__), d, s, n); \
} typedef void dummy_type
void sink (void*, ...);
// Exercise string functions.
T (index, a, 'x'); // { dg-warning "missing terminating nul" "index" }
T (index, a, *s); // { dg-warning "missing terminating nul" "index" }
T (index, b, '0');
T (index, b + 1, '1');
T (index, b + 2, '2');
T (index, b + 3, '3');
T (index, b + 4, '4'); // { dg-warning "missing terminating nul" "index" }
T (rindex, a, 'x'); // { dg-warning "missing terminating nul" "rindex" }
T (rindex, a, *s); // { dg-warning "missing terminating nul" "rindex" }
T (rindex, b, '0');
T (rindex, b + 1, '1');
T (rindex, b + 2, '2');
T (rindex, b + 3, '3');
T (rindex, b + 4, '4'); // { dg-warning "missing terminating nul" "rindex" }
T (stpcpy, d, a); // { dg-warning "missing terminating nul" "stpcpy" }
T (stpncpy, d, a, 4);
T (stpncpy, d, a, 5); // { dg-warning "missing terminating nul" "stpncpy" }
T (stpncpy, d, a, n);
T (stpncpy, d, a + n, 4);
T (stpncpy, d, a + n, 5); // { dg-warning "missing terminating nul" "stpncpy" }
T (stpncpy, d, b, 4);
T (stpncpy, d, b, 5);
T (stpncpy, d, b, n);
T (stpncpy, d, b + 1, 4);
T (stpncpy, d, b + 1, 5);
T (stpncpy, d, b + 1, n);
T (stpncpy, d, b + 3, 4);
T (stpncpy, d, b + 3, 5);
T (stpncpy, d, b + 3, n);
T (stpncpy, d, b + 4, 1);
T (stpncpy, d, b + 4, 2); // { dg-warning "missing terminating nul" "stpncpy" }
T (stpncpy, d, b + 4, n);
/* The following might be worth warning about since it's only safe with
n < 4. */
T (stpncpy, d, b + n, 5);
T (strcasecmp, a, "ab"); // { dg-warning "missing terminating nul" "strcasecmp" }
T (strcasecmp, a, s); // { dg-warning "missing terminating nul" "strcasecmp" }
T (strcasecmp, a, b); // { dg-warning "missing terminating nul" "strcasecmp" }
T (strcasecmp, b, b + 1);
T (strcasecmp, b, b + 2);
T (strcasecmp, b, b + 3);
T (strcasecmp, b, b + 4); // { dg-warning "missing terminating nul" "strcasecmp" }
T (strcat, d, a); // { dg-warning "missing terminating nul" "strcat" }
T (strncat, d, a, 4);
T (strncat, d, a, 5); // { dg-warning "missing terminating nul" "strncat" }
T (strncat, d, a, n);
T (strncat, d, b, n);
T (strncat, d, b + 1, n);
T (strncat, d, b + 2, n);
T (strncat, d, b + 3, n);
T (strncat, d, b + 4, 0);
T (strncat, d, b + 4, 1);
T (strncat, d, b + 4, 2); // { dg-warning "missing terminating nul" "strncat" }
/* The following should probably trigger a warning since it's only safe
when n < 2, makes little sense with n == 0, and not much more with
n == 1. */
T (strncat, d, b + 4, n); // { dg-warning "missing terminating nul" "strncat" { xfail *-*-* } }
T (strchr, a, 'x'); // { dg-warning "missing terminating nul" "strchr" }
T (strchr, a, *s); // { dg-warning "missing terminating nul" "strchr" }
T (strcmp, a, "ab"); // { dg-warning "missing terminating nul" "strcmp" }
T (strcmp, "bc", a); // { dg-warning "missing terminating nul" "strcmp" }
T (strcmp, a, s); // { dg-warning "missing terminating nul" "strcmp" }
T (strcmp, s, a); // { dg-warning "missing terminating nul" "strcmp" }
T (strcmp, a, b); // { dg-warning "missing terminating nul" "strcmp" }
/* Even though most likely safe in reality because b[1] is nul,
the following is strictly undefined because a is not a string.
The warning is not issued because GCC folds the call to (int)*a. */
T (strcmp, a, b + 1); // { dg-warning "missing terminating nul" "bug" { xfail *-*-* } }
T (strncmp, a, "ab", 4);
T (strncmp, "bc", a, 4);
T (strncmp, a, a, 4);
T (strncmp, a, s, 4);
T (strncmp, s, a, 4);
/* The warning below is not issued because GCC folds strncmp calls with
the same arguments to zero before it checks for the missing nul. */
T (strncmp, a, a, 5); // { dg-warning "missing terminating nul" "pr92624" { xfail *-*-*} }
T (strncmp, a, s, 5); // { dg-warning "missing terminating nul" "strcmp" }
T (strncmp, s, a, 5); // { dg-warning "missing terminating nul" "strcmp" }
T (strcpy, d, a); // { dg-warning "missing terminating nul" "strcpy" }
T (strcspn, a, s); // { dg-warning "missing terminating nul" "strcspn" }
T (strcspn, s, a); // { dg-warning "missing terminating nul" "strcspn" }
T (strspn, a, s); // { dg-warning "missing terminating nul" "strcspn" }
T (strspn, s, a); // { dg-warning "missing terminating nul" "strcspn" }
T (strdup, a); // { dg-warning "missing terminating nul" "strdup" }
T (strndup, a, 4);
T (strndup, a, 5); // { dg-warning "missing terminating nul" "strndup" }
T (strndup, b + 3, 2);
T (strndup, b + 4, 1);
T (strndup, b + 4, 2); // { dg-warning "missing terminating nul" "strndup" }
T (strlen, a); // { dg-warning "missing terminating nul" "strlen" }
T (strnlen, a, 4);
T (strnlen, a, 5); // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strnlen" }
T (strnlen, a, n);
T (strpbrk, s, a); // { dg-warning "missing terminating nul" "strpbrk" }
T (strrchr, a, 'x'); // { dg-warning "missing terminating nul" "strrchr" }
T (strrchr, a, *s); // { dg-warning "missing terminating nul" "strrchr" }
T (strstr, a, "cde"); // { dg-warning "missing terminating nul" "strstr" }
T (strstr, a, s); // { dg-warning "missing terminating nul" "strstr" }
// Exercise a few string checking functions.
T (__stpcpy_chk, d, a, -1); // { dg-warning "missing terminating nul" "stpcpy" }
T (__stpncpy_chk, d, a, 4, -1);
T (__stpncpy_chk, d, a, 5, -1); // { dg-warning "missing terminating nul" "stpncpy_chk" }
T (__stpncpy_chk, d, a, n, -1);
T (__stpncpy_chk, d, a + n, 4, -1);
T (__stpncpy_chk, d, a + n, 5, -1); // { dg-warning "missing terminating nul" "stpncpy_chk" }
T (__stpncpy_chk, d, b, 4, -1);
T (__stpncpy_chk, d, b, 5, -1);
T (__stpncpy_chk, d, b, n, -1);
T (__stpncpy_chk, d, b + 1, 4, -1);
T (__stpncpy_chk, d, b + 1, 5, -1);
T (__stpncpy_chk, d, b + 1, n, -1);
T (__stpncpy_chk, d, b + 3, 4, -1);
T (__stpncpy_chk, d, b + 3, 5, -1);
T (__stpncpy_chk, d, b + 3, n, -1);
T (__stpncpy_chk, d, b + 4, 1, -1);
T (__stpncpy_chk, d, b + 4, 2, -1); // { dg-warning "missing terminating nul" "stpncpy_chk" }
T (__stpncpy_chk, d, b + 4, n, -1);
T (__strncat_chk, d, a, 4, -1);
T (__strncat_chk, d, a, 5, -1); // { dg-warning "missing terminating nul" "strncat_chk" }
T (__strncat_chk, d, a, n, -1);
T (__strncat_chk, d, a + n, 4, -1);
T (__strncat_chk, d, a + n, 5, -1); // { dg-warning "missing terminating nul" "strncat_chk" }
T (__strncat_chk, d, b, 4, -1);
T (__strncat_chk, d, b, 5, -1);
T (__strncat_chk, d, b, n, -1);
T (__strncat_chk, d, b + 1, 4, -1);
T (__strncat_chk, d, b + 1, 5, -1);
T (__strncat_chk, d, b + 1, n, -1);
T (__strncat_chk, d, b + 3, 4, -1);
T (__strncat_chk, d, b + 3, 5, -1);
T (__strncat_chk, d, b + 3, n, -1);
T (__strncat_chk, d, b + 4, 1, -1);
T (__strncat_chk, d, b + 4, 2, -1); // { dg-warning "missing terminating nul" "strncat_chk" }
T (__strncat_chk, d, b + 4, n, -1);
T (__strncpy_chk, d, a, 4, -1);
T (__strncpy_chk, d, a, 5, -1); // { dg-warning "missing terminating nul" "strncpy_chk" }
T (__strncpy_chk, d, a, n, -1);
T (__strncpy_chk, d, a + n, 4, -1);
T (__strncpy_chk, d, a + n, 5, -1); // { dg-warning "missing terminating nul" "strncpy_chk" }
T (__strncpy_chk, d, b, 4, -1);
T (__strncpy_chk, d, b, 5, -1);
T (__strncpy_chk, d, b, n, -1);
T (__strncpy_chk, d, b + 1, 4, -1);
T (__strncpy_chk, d, b + 1, 5, -1);
T (__strncpy_chk, d, b + 1, n, -1);
T (__strncpy_chk, d, b + 3, 4, -1);
T (__strncpy_chk, d, b + 3, 5, -1);
T (__strncpy_chk, d, b + 3, n, -1);
T (__strncpy_chk, d, b + 4, 1, -1);
T (__strncpy_chk, d, b + 4, 2, -1); // { dg-warning "missing terminating nul" "strncpy" }
T (__strncpy_chk, d, b + 4, n, -1);
// Exercise some stdio functions.
T (printf, a); // { dg-warning "unterminated format string" "printf" }
T (printf, "%s", a); // { dg-warning "not a nul-terminated string" "printf" }
T (sprintf, d, "%s", a); // { dg-warning "not a nul-terminated string" "sprintf" }
T (snprintf, d, n, "%s", a); // { dg-warning "not a nul-terminated string" "sprintf" }
T (__sprintf_chk, d, 0, -1, "%s", a); // { dg-warning "not a nul-terminated string" "sprintf" }
T (__snprintf_chk, d, n, 0, -1, "%s", a); // { dg-warning "not a nul-terminated string" "sprintf" }
T (fputs, a, fp); // { dg-warning "missing terminating nul" "fputs" }
T (fputs_unlocked, a, fp); // { dg-warning "missing terminating nul" "fputs_unlocked" }
T (puts, a); // { dg-warning "missing terminating nul" "puts" }
T (puts_unlocked, a); // { dg-warning "missing terminating nul" "puts_unlocked" }
// Exerise exec functions.
T (execl, a, s, NULL); // { dg-warning "missing terminating nul" "execl" }
T (execl, a, s, NULL); // { dg-warning "missing terminating nul" "execl" }
T (execle, a, s, NULL, NULL); // { dg-warning "missing terminating nul" "execl" }
T (execlp, a, s, NULL); // { dg-warning "missing terminating nul" "execl" }
T (execv, a, &d); // { dg-warning "missing terminating nul" "execl" }
T (execve, a, &d, &d); // { dg-warning "missing terminating nul" "execl" }
T (execvp, a, &d); // { dg-warning "missing terminating nul" "execl" }
T (gettext, a); // { dg-warning "missing terminating nul" "gettext" }
T (strfmon, d, n, a); // { dg-warning "unterminated format string" "strfmon" }
...@@ -84,8 +84,8 @@ void test_fprintf_s_const (int width) ...@@ -84,8 +84,8 @@ void test_fprintf_s_const (int width)
if (nulptr) if (nulptr)
T ("%s", nulptr); T ("%s", nulptr);
T ("%s", &chr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" "pr88226" { xfail *-*-* } } */ T ("%s", &chr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string|argument missing terminating nul" } */
T ("%s", arr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" "pr88226" { xfail *-*-* } } */ T ("%s", arr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string|argument missing terminating nul" } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even /* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */ when the size of the destination object is unknown. */
...@@ -117,7 +117,7 @@ void test_fprintf_ls_const (int width) ...@@ -117,7 +117,7 @@ void test_fprintf_ls_const (int width)
T ("%ls", nulptr); T ("%ls", nulptr);
T ("%ls", &wchr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" } */ T ("%ls", &wchr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" } */
T ("%ls", warr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr88211" { xfail *-*-* } } */ T ("%ls", warr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr88226" { xfail *-*-* } } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even /* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */ when the size of the destination object is unknown. */
......
...@@ -1946,8 +1946,6 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) ...@@ -1946,8 +1946,6 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
static void static void
handle_builtin_strchr (gimple_stmt_iterator *gsi) handle_builtin_strchr (gimple_stmt_iterator *gsi)
{ {
int idx;
tree src;
gimple *stmt = gsi_stmt (*gsi); gimple *stmt = gsi_stmt (*gsi);
tree lhs = gimple_call_lhs (stmt); tree lhs = gimple_call_lhs (stmt);
...@@ -1957,8 +1955,14 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi) ...@@ -1957,8 +1955,14 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
if (!integer_zerop (gimple_call_arg (stmt, 1))) if (!integer_zerop (gimple_call_arg (stmt, 1)))
return; return;
src = gimple_call_arg (stmt, 0); tree src = gimple_call_arg (stmt, 0);
idx = get_stridx (src);
/* Avoid folding if the first argument is not a nul-terminated array.
Defer warning until later. */
if (!check_nul_terminated_array (NULL_TREE, src))
return;
int idx = get_stridx (src);
if (idx) if (idx)
{ {
strinfo *si = NULL; strinfo *si = NULL;
...@@ -3794,11 +3798,11 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi) ...@@ -3794,11 +3798,11 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
/* For strncmp set to the the value of the third argument if known. */ /* For strncmp set to the the value of the third argument if known. */
HOST_WIDE_INT bound = -1; HOST_WIDE_INT bound = -1;
tree len = NULL_TREE;
/* Extract the strncmp bound. */ /* Extract the strncmp bound. */
if (gimple_call_num_args (stmt) == 3) if (gimple_call_num_args (stmt) == 3)
{ {
tree len = gimple_call_arg (stmt, 2); len = gimple_call_arg (stmt, 2);
if (tree_fits_shwi_p (len)) if (tree_fits_shwi_p (len))
bound = tree_to_shwi (len); bound = tree_to_shwi (len);
...@@ -3807,6 +3811,12 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi) ...@@ -3807,6 +3811,12 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
return false; return false;
} }
/* Avoid folding if either argument is not a nul-terminated array.
Defer warning until later. */
if (!check_nul_terminated_array (NULL_TREE, arg1, len)
|| !check_nul_terminated_array (NULL_TREE, arg2, len))
return false;
{ {
/* Set to the length of one argument (or its complement if it's /* Set to the length of one argument (or its complement if it's
the lower bound of a range) and the size of the array storing the lower bound of a range) and the size of the array storing
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment