PR c/78673 - sprintf missing attribute nonnull on destination argument

PR c/78673 - sprintf missing attribute nonnull on destination argument
PR c/17308 - nonnull attribute not as useful as it could be

gcc/ChangeLog:

	PR c/17308
	* builtin-attrs.def (ATTR_NONNULL_1_1, ATTR_NONNULL_1_2): Defined.
	(ATTR_NONNULL_1_3, ATTR_NONNULL_1_4, ATTR_NONNULL_1_5): Same.
	(ATTR_NOTHROW_NONNULL_1_1, ATTR_NOTHROW_NONNULL_1_2): Same.
	(ATTR_NOTHROW_NONNULL_1_3, ATTR_NOTHROW_NONNULL_1_4): Same.
	(ATTR_NOTHROW_NONNULL_1_5): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_1_2): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_2_0): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_2_3): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_3_0): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_3_4): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_4_0): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_4_5): Same.
	* builtins.c (validate_arg): Add argument.  Treat null pointers
	passed to nonnull arguments as invalid.
	(validate_arglist): Same.
	* builtins.def (fprintf, fprintf_unlocked): Add nonnull attribute.
	(printf, printf_unlocked, sprintf. vfprintf, vsprintf): Same.
	(__sprintf_chk, __vsprintf_chk, __fprintf_chk, __vfprintf_chk): Same.
	* calls.c (get_nonnull_ags, maybe_warn_null_arg): New functions.
	(initialize_argument_information): Diagnose null pointers passed to
	arguments declared nonnull.
	* calls.h (get_nonnull_args): Declared.

gcc/c-family/ChangeLog:

	PR c/17308
	* c-common.c (check_nonnull_arg): Disable when optimization
	is enabled.

gcc/testsuite/ChangeLog:

	PR c/17308
	* gcc.dg/builtins-nonnull.c: New test.
	* gcc.dg/nonnull-4.c: New test.

From-SVN: r243661
This commit is contained in:
Martin Sebor 2016-12-14 17:23:16 +00:00 committed by Martin Sebor
parent b4ba085209
commit 474da67ef9
11 changed files with 566 additions and 20 deletions

View File

@ -1,3 +1,29 @@
2016-12-14 Martin Sebor <msebor@redhat.com>
PR c/17308
* builtin-attrs.def (ATTR_NONNULL_1_1, ATTR_NONNULL_1_2): Defined.
(ATTR_NONNULL_1_3, ATTR_NONNULL_1_4, ATTR_NONNULL_1_5): Same.
(ATTR_NOTHROW_NONNULL_1_1, ATTR_NOTHROW_NONNULL_1_2): Same.
(ATTR_NOTHROW_NONNULL_1_3, ATTR_NOTHROW_NONNULL_1_4): Same.
(ATTR_NOTHROW_NONNULL_1_5): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_1_2): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_2_0): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_2_3): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_3_0): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_3_4): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_4_0): Same.
(ATTR_NONNULL_1_FORMAT_PRINTF_4_5): Same.
* builtins.c (validate_arg): Add argument. Treat null pointers
passed to nonnull arguments as invalid.
(validate_arglist): Same.
* builtins.def (fprintf, fprintf_unlocked): Add nonnull attribute.
(printf, printf_unlocked, sprintf. vfprintf, vsprintf): Same.
(__sprintf_chk, __vsprintf_chk, __fprintf_chk, __vfprintf_chk): Same.
* calls.c (get_nonnull_ags, maybe_warn_null_arg): New functions.
(initialize_argument_information): Diagnose null pointers passed to
arguments declared nonnull.
* calls.h (get_nonnull_args): Declared.
2016-12-14 Michael Meissner <meissner@linux.vnet.ibm.com>
* config/rs6000/rs6000.c (rs6000_split_vec_extract_var): On ISA

View File

@ -72,6 +72,9 @@ DEF_ATTR_FOR_STRING (STR1, "1")
ATTR_##VALUE1, ATTR_LIST_##VALUE2)
DEF_LIST_INT_INT (1,0)
DEF_LIST_INT_INT (1,2)
DEF_LIST_INT_INT (1,3)
DEF_LIST_INT_INT (1,4)
DEF_LIST_INT_INT (1,5)
DEF_LIST_INT_INT (2,0)
DEF_LIST_INT_INT (2,3)
DEF_LIST_INT_INT (3,0)
@ -205,6 +208,40 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_4, ATTR_NONNULL, ATTR_LIST_4, \
/* Nothrow functions whose fifth parameter is a nonnull pointer. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_5, ATTR_NONNULL, ATTR_LIST_5, \
ATTR_NOTHROW_LIST)
/* Same as ATTR_NONNULL_1. */
DEF_ATTR_TREE_LIST (ATTR_NONNULL_1_1, ATTR_NONNULL, ATTR_LIST_1, ATTR_NULL)
/* Functions like {v,}fprintf whose first and second parameters are
nonnull pointers. As cancellation points the functions are not
nothrow. */
DEF_ATTR_TREE_LIST (ATTR_NONNULL_1_2, ATTR_NONNULL, ATTR_LIST_1_2, ATTR_NULL)
/* The following don't have {v,}fprintf forms. They exist only to
make it possible to declare {v,}{f,s}printf attributes using
the same macro. */
DEF_ATTR_TREE_LIST (ATTR_NONNULL_1_3, ATTR_NONNULL, ATTR_LIST_1_3, ATTR_NULL)
DEF_ATTR_TREE_LIST (ATTR_NONNULL_1_4, ATTR_NONNULL, ATTR_LIST_1_4, ATTR_NULL)
DEF_ATTR_TREE_LIST (ATTR_NONNULL_1_5, ATTR_NONNULL, ATTR_LIST_1_5, ATTR_NULL)
/* Same as ATTR_NOTHROW_NONNULL_1. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_1, ATTR_NONNULL, ATTR_LIST_1,
ATTR_NOTHROW_LIST)
/* Nothrow functions like {v,}sprintf whose first and second parameters
are nonnull pointers. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_2, ATTR_NONNULL, ATTR_LIST_1_2, \
ATTR_NOTHROW_LIST)
/* Nothrow functions like {v,}snprintf whose first and third parameters
are nonnull pointers. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_3, ATTR_NONNULL, ATTR_LIST_1_3, \
ATTR_NOTHROW_LIST)
/* Nothrow functions like {v,}sprintf_chk whose first and fourth parameters
are nonnull pointers. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_4, ATTR_NONNULL, ATTR_LIST_1_4, \
ATTR_NOTHROW_LIST)
/* Nothrow functions like {v,}snprintf_chk whose first and fifth parameters
are nonnull pointers. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_5, ATTR_NONNULL, ATTR_LIST_1_5, \
ATTR_NOTHROW_LIST)
/* Nothrow leaf functions which are type-generic. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_TYPEGENERIC_LEAF, ATTR_TYPEGENERIC, ATTR_NULL, \
ATTR_NOTHROW_LEAF_LIST)
@ -245,17 +282,23 @@ DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_NONNULL, ATTR_MALLOC, ATTR_NULL, \
DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_NONNULL_LEAF, ATTR_MALLOC, ATTR_NULL, \
ATTR_NOTHROW_NONNULL_LEAF)
/* Construct a tree for a format attribute. */
/* Construct a tree for the format attribute (and implicitly nonnull). */
#define DEF_FORMAT_ATTRIBUTE(TYPE, FA, VALUES) \
DEF_ATTR_TREE_LIST (ATTR_##TYPE##_##VALUES, ATTR_NULL, \
ATTR_##TYPE, ATTR_LIST_##VALUES) \
DEF_ATTR_TREE_LIST (ATTR_FORMAT_##TYPE##_##VALUES, ATTR_FORMAT, \
ATTR_##TYPE##_##VALUES, ATTR_NONNULL_##FA)
/* Construct a tree for the format and nothrow attributes (format
implies nonnull). */
#define DEF_FORMAT_ATTRIBUTE_NOTHROW(TYPE, FA, VALUES) \
DEF_ATTR_TREE_LIST (ATTR_##TYPE##_##VALUES, ATTR_NULL, \
ATTR_##TYPE, ATTR_LIST_##VALUES) \
DEF_ATTR_TREE_LIST (ATTR_FORMAT_##TYPE##_NOTHROW_##VALUES, ATTR_FORMAT,\
ATTR_##TYPE##_##VALUES, ATTR_NOTHROW_NONNULL_##FA)
/* Construct one tree for the format attribute and another for the format
and nothrow attributes (in both cases format implies nonnull). */
#define DEF_FORMAT_ATTRIBUTE_BOTH(TYPE, FA, VALUES) \
DEF_ATTR_TREE_LIST (ATTR_##TYPE##_##VALUES, ATTR_NULL, \
ATTR_##TYPE, ATTR_LIST_##VALUES) \
@ -263,6 +306,18 @@ DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_NONNULL_LEAF, ATTR_MALLOC, ATTR_NULL, \
ATTR_##TYPE##_##VALUES, ATTR_NONNULL_##FA) \
DEF_ATTR_TREE_LIST (ATTR_FORMAT_##TYPE##_NOTHROW_##VALUES, ATTR_FORMAT,\
ATTR_##TYPE##_##VALUES, ATTR_NOTHROW_NONNULL_##FA)
/* Construct a pair of trees for the nonnull attribute for the first
argument, plus format printf attribute (format implies nonnull):
the first ordinary and the second nothrow. */
#define DEF_FORMAT_ATTRIBUTE_NONNULL(TYPE, FA, VALUES) \
DEF_ATTR_TREE_LIST (ATTR_NONNULL_1_FORMAT_##TYPE##_##VALUES, \
ATTR_FORMAT, ATTR_##TYPE##_##VALUES, \
ATTR_NONNULL_1_##FA) \
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_FORMAT_##TYPE##_##VALUES, \
ATTR_FORMAT, ATTR_##TYPE##_##VALUES, \
ATTR_NOTHROW_NONNULL_1_##FA)
DEF_FORMAT_ATTRIBUTE(PRINTF,1,1_0)
DEF_FORMAT_ATTRIBUTE(PRINTF,1,1_2)
DEF_FORMAT_ATTRIBUTE_BOTH(PRINTF,2,2_0)
@ -273,6 +328,26 @@ DEF_FORMAT_ATTRIBUTE_NOTHROW(PRINTF,4,4_0)
DEF_FORMAT_ATTRIBUTE_NOTHROW(PRINTF,4,4_5)
DEF_FORMAT_ATTRIBUTE_NOTHROW(PRINTF,5,5_0)
DEF_FORMAT_ATTRIBUTE_NOTHROW(PRINTF,5,5_6)
/* Attributes for fprintf(f, f, va). */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,1,1_2)
/* Attributes for v{f,s}printf(d, f, va). vsprintf is nothrow, vfprintf
is not. */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,2,2_0)
/* Attributes for {f,s}printf(d, f, ...). sprintf is nothrow, fprintf
is not. */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,2,2_3)
/* Attributes for vprintf_chk. */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,3,3_0)
/* Attributes for printf_chk. */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,3,3_4)
/* Attributes for v{f,s}printf_chk(d, t, bos, f, va). vsprintf_chk is
nothrow, vfprintf_chk is not. */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,4,4_0)
/* Attributes for {f,s}printf_chk(d, t, bos, f, ...). sprintf_chk is
nothrow, fprintf_chk is not. */
DEF_FORMAT_ATTRIBUTE_NONNULL(PRINTF,4,4_5)
DEF_FORMAT_ATTRIBUTE(SCANF,1,1_0)
DEF_FORMAT_ATTRIBUTE(SCANF,1,1_2)
DEF_FORMAT_ATTRIBUTE_BOTH(SCANF,2,2_0)

View File

@ -147,7 +147,7 @@ static tree fold_builtin_classify_type (tree);
static tree fold_builtin_strlen (location_t, tree, tree);
static tree fold_builtin_inf (location_t, tree, int);
static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
static bool validate_arg (const_tree, enum tree_code code);
static bool validate_arg (const_tree, enum tree_code code, bool = false);
static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_signbit (tree, rtx);
static tree fold_builtin_memcmp (location_t, tree, tree, tree);
@ -1034,7 +1034,7 @@ more_const_call_expr_args_p (const const_call_expr_arg_iterator *iter)
/* This function validates the types of a function call argument list
against a specified list of tree_codes. If the last specifier is a 0,
that represents an ellipses, otherwise the last specifier must be a
that represents an ellipsis, otherwise the last specifier must be a
VOID_TYPE. */
static bool
@ -1049,9 +1049,14 @@ validate_arglist (const_tree callexpr, ...)
va_start (ap, callexpr);
init_const_call_expr_arg_iterator (callexpr, &iter);
do
/* Get a bitmap of pointer argument numbers declared attribute nonnull. */
bitmap argmap = get_nonnull_args (callexpr);
for (unsigned argno = 1; ; ++argno)
{
code = (enum tree_code) va_arg (ap, int);
bool nonnull = false;
switch (code)
{
case 0:
@ -1063,23 +1068,31 @@ validate_arglist (const_tree callexpr, ...)
true, otherwise return false. */
res = !more_const_call_expr_args_p (&iter);
goto end;
case POINTER_TYPE:
/* The actual argument must be nonnull when either the whole
called function has been declared nonnull, or when the formal
argument corresponding to the actual argument has been. */
if (argmap)
nonnull = bitmap_empty_p (argmap) || bitmap_bit_p (argmap, argno);
/* FALLTHRU */
default:
/* If no parameters remain or the parameter's code does not
match the specified code, return false. Otherwise continue
checking any remaining arguments. */
arg = next_const_call_expr_arg (&iter);
if (!validate_arg (arg, code))
if (!validate_arg (arg, code, nonnull))
goto end;
break;
}
}
while (1);
/* We need gotos here since we can only have one VA_CLOSE in a
function. */
end: ;
va_end (ap);
BITMAP_FREE (argmap);
return res;
}
@ -9121,15 +9134,17 @@ rewrite_call_expr (location_t loc, tree exp, int skip, tree fndecl, int n, ...)
}
/* Validate a single argument ARG against a tree code CODE representing
a type. */
a type. When NONNULL is true consider a pointer argument valid only
if it's non-null. Return true when argument is valid. */
static bool
validate_arg (const_tree arg, enum tree_code code)
validate_arg (const_tree arg, enum tree_code code, bool nonnull /*= false*/)
{
if (!arg)
return false;
else if (code == POINTER_TYPE)
return POINTER_TYPE_P (TREE_TYPE (arg));
return POINTER_TYPE_P (TREE_TYPE (arg))
&& (!nonnull || !integer_zerop (arg));
else if (code == INTEGER_TYPE)
return INTEGRAL_TYPE_P (TREE_TYPE (arg));
return code == TREE_CODE (TREE_TYPE (arg));

View File

@ -683,8 +683,8 @@ DEF_LIB_BUILTIN (BUILT_IN_STRSPN, "strspn", BT_FN_SIZE_CONST_STRING_CONST
DEF_LIB_BUILTIN (BUILT_IN_STRSTR, "strstr", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
/* Category: stdio builtins. */
DEF_LIB_BUILTIN (BUILT_IN_FPRINTF, "fprintf", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_2_3)
DEF_EXT_LIB_BUILTIN (BUILT_IN_FPRINTF_UNLOCKED, "fprintf_unlocked", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_2_3)
DEF_LIB_BUILTIN (BUILT_IN_FPRINTF, "fprintf", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_2_3)
DEF_EXT_LIB_BUILTIN (BUILT_IN_FPRINTF_UNLOCKED, "fprintf_unlocked", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_2_3)
DEF_LIB_BUILTIN (BUILT_IN_PUTC, "putc", BT_FN_INT_INT_FILEPTR, ATTR_NONNULL_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_PUTC_UNLOCKED, "putc_unlocked", BT_FN_INT_INT_FILEPTR, ATTR_NONNULL_LIST)
DEF_LIB_BUILTIN (BUILT_IN_FPUTC, "fputc", BT_FN_INT_INT_FILEPTR, ATTR_NONNULL_LIST)
@ -695,21 +695,22 @@ DEF_LIB_BUILTIN (BUILT_IN_FSCANF, "fscanf", BT_FN_INT_FILEPTR_CONST_STRIN
DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_FWRITE_UNLOCKED, "fwrite_unlocked", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST)
DEF_LIB_BUILTIN (BUILT_IN_PRINTF, "printf", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_1_2)
DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_UNLOCKED, "printf_unlocked", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_1_2)
DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_UNLOCKED, "printf_unlocked", BT_FN_INT_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_1_2)
DEF_LIB_BUILTIN (BUILT_IN_PUTCHAR, "putchar", BT_FN_INT_INT, ATTR_NULL)
DEF_EXT_LIB_BUILTIN (BUILT_IN_PUTCHAR_UNLOCKED, "putchar_unlocked", BT_FN_INT_INT, ATTR_NULL)
DEF_LIB_BUILTIN (BUILT_IN_PUTS, "puts", BT_FN_INT_CONST_STRING, ATTR_NONNULL_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_PUTS_UNLOCKED, "puts_unlocked", BT_FN_INT_CONST_STRING, ATTR_NONNULL_LIST)
DEF_LIB_BUILTIN (BUILT_IN_SCANF, "scanf", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_SCANF_1_2)
DEF_C99_BUILTIN (BUILT_IN_SNPRINTF, "snprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_3_4)
DEF_LIB_BUILTIN (BUILT_IN_SPRINTF, "sprintf", BT_FN_INT_STRING_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_2_3)
DEF_LIB_BUILTIN (BUILT_IN_SPRINTF, "sprintf", BT_FN_INT_STRING_CONST_STRING_VAR, ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_2_3)
DEF_LIB_BUILTIN (BUILT_IN_SSCANF, "sscanf", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_FORMAT_SCANF_NOTHROW_2_3)
DEF_LIB_BUILTIN (BUILT_IN_VFPRINTF, "vfprintf", BT_FN_INT_FILEPTR_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0)
DEF_LIB_BUILTIN (BUILT_IN_VFPRINTF, "vfprintf", BT_FN_INT_FILEPTR_CONST_STRING_VALIST_ARG, ATTR_NONNULL_1_FORMAT_PRINTF_2_0)
DEF_C99_BUILTIN (BUILT_IN_VFSCANF, "vfscanf", BT_FN_INT_FILEPTR_CONST_STRING_VALIST_ARG, ATTR_FORMAT_SCANF_2_0)
DEF_LIB_BUILTIN (BUILT_IN_VPRINTF, "vprintf", BT_FN_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_1_0)
DEF_C99_BUILTIN (BUILT_IN_VSCANF, "vscanf", BT_FN_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_SCANF_1_0)
DEF_C99_BUILTIN (BUILT_IN_VSNPRINTF, "vsnprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_NOTHROW_3_0)
DEF_LIB_BUILTIN (BUILT_IN_VSPRINTF, "vsprintf", BT_FN_INT_STRING_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_NOTHROW_2_0)
DEF_LIB_BUILTIN (BUILT_IN_VSPRINTF, "vsprintf", BT_FN_INT_STRING_CONST_STRING_VALIST_ARG, ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_2_0)
DEF_C99_BUILTIN (BUILT_IN_VSSCANF, "vsscanf", BT_FN_INT_CONST_STRING_CONST_STRING_VALIST_ARG, ATTR_FORMAT_SCANF_NOTHROW_2_0)
/* Category: ctype builtins. */
@ -926,12 +927,12 @@ DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRI
DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCAT_CHK, "__strncat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_SNPRINTF_CHK, "__snprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_5_6)
DEF_EXT_LIB_BUILTIN (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_4_5)
DEF_EXT_LIB_BUILTIN (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_4_5)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VSNPRINTF_CHK, "__vsnprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_NOTHROW_5_0)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VSPRINTF_CHK, "__vsprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_NOTHROW_4_0)
DEF_EXT_LIB_BUILTIN (BUILT_IN_FPRINTF_CHK, "__fprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_3_4)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VSPRINTF_CHK, "__vsprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_4_0)
DEF_EXT_LIB_BUILTIN (BUILT_IN_FPRINTF_CHK, "__fprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_3_4)
DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_CHK, "__printf_chk", BT_FN_INT_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_2_3)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VFPRINTF_CHK, "__vfprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_3_0)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VFPRINTF_CHK, "__vfprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, ATTR_NONNULL_1_FORMAT_PRINTF_3_0)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VPRINTF_CHK, "__vprintf_chk", BT_FN_INT_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0)
/* Profiling hooks. */

View File

@ -1,3 +1,9 @@
2016-12-14 Martin Sebor <msebor@redhat.com>
PR c/17308
* c-common.c (check_nonnull_arg): Disable when optimization
is enabled.
2016-12-12 Marek Polacek <polacek@redhat.com>
PR c++/78647

View File

@ -5388,7 +5388,10 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
if (TREE_CODE (TREE_TYPE (param)) != POINTER_TYPE)
return;
if (integer_zerop (param))
/* When not optimizing diagnose the simple cases of null arguments.
When optimization is enabled defer the checking until expansion
when more cases can be detected. */
if (!optimize && integer_zerop (param))
warning_at (*ploc, OPT_Wnonnull, "null argument where non-null required "
"(argument %lu)", (unsigned long) param_num);
}

View File

@ -1501,6 +1501,91 @@ maybe_complain_about_tail_call (tree call_expr, const char *reason)
error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
}
/* Return a bitmap with a bit set corresponding to each argument in
a function call expression CALLEXPR declared with attribute nonnull,
or null if none of the function's argument are nonnull. The caller
must free the bitmap. */
bitmap
get_nonnull_args (const_tree callexpr)
{
tree fn = CALL_EXPR_FN (callexpr);
if (!fn || TREE_CODE (fn) != ADDR_EXPR)
return NULL;
tree fndecl = TREE_OPERAND (fn, 0);
tree fntype = TREE_TYPE (fndecl);
tree attrs = TYPE_ATTRIBUTES (fntype);
if (!attrs)
return NULL;
bitmap argmap = NULL;
/* A function declaration can specify multiple attribute nonnull,
each with zero or more arguments. The loop below creates a bitmap
representing a union of all the arguments. An empty (but non-null)
bitmap means that all arguments have been declaraed nonnull. */
for ( ; attrs; attrs = TREE_CHAIN (attrs))
{
attrs = lookup_attribute ("nonnull", attrs);
if (!attrs)
break;
if (!argmap)
argmap = BITMAP_ALLOC (NULL);
if (!TREE_VALUE (attrs))
{
/* Clear the bitmap in case a previous attribute nonnull
set it and this one overrides it for all arguments. */
bitmap_clear (argmap);
return argmap;
}
/* Iterate over the indices of the format arguments declared nonnull
and set a bit for each. */
for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
{
unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
bitmap_set_bit (argmap, val);
}
}
return argmap;
}
/* In a call EXP to a function FNDECL some of whose arguments may have
been declared with attribute nonnull as described by NONNULLARGS,
check actual argument ARG at the zero-based position ARGPOS for
equality to null and issue a warning if it is not expected to be. */
static void
maybe_warn_null_arg (tree fndecl, tree exp, tree arg,
unsigned argpos, bitmap nonnullargs)
{
if (!optimize
|| !nonnullargs
|| TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE
|| !integer_zerop (arg)
|| (!bitmap_empty_p (nonnullargs)
&& !bitmap_bit_p (nonnullargs, argpos)))
return;
++argpos;
location_t exploc EXPR_LOCATION (exp);
if (warning_at (exploc, OPT_Wnonnull,
"argument %u null where non-null expected", argpos))
{
if (DECL_IS_BUILTIN (fndecl))
inform (exploc, "in a call to built-in function %qD", fndecl);
else
inform (DECL_SOURCE_LOCATION (fndecl),
"in a call to function %qD declared here", fndecl);
}
}
/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
CALL_EXPR EXP.
@ -1684,6 +1769,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* Array for up to the two attribute alloc_size arguments. */
tree alloc_args[] = { NULL_TREE, NULL_TREE };
/* Get a bitmap of pointer argument numbers declared attribute nonnull. */
bitmap nonnullargs = get_nonnull_args (exp);
/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
for (argpos = 0; argpos < num_actuals; i--, argpos++)
{
@ -1915,6 +2003,11 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
if (args[i].locate.size.var)
ADD_PARM_SIZE (*args_size, args[i].locate.size.var);
/* Check pointer argument for equality to NULL that is being passed
to arguments declared with attribute nonnull and warn. */
maybe_warn_null_arg (fndecl, exp, args[i].tree_value, argpos,
nonnullargs);
/* Increment ARGS_SO_FAR, which has info about which arg-registers
have been used, etc. */
@ -1935,6 +2028,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
alloc_size. */
maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx);
}
BITMAP_FREE (nonnullargs);
}
/* Update ARGS_SIZE to contain the total size for the argument block.

View File

@ -38,5 +38,6 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
tree, bool);
extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
extern bitmap get_nonnull_args (const_tree);
#endif // GCC_CALLS_H

View File

@ -1,3 +1,9 @@
2016-12-14 Martin Sebor <msebor@redhat.com>
PR c/17308
* gcc.dg/builtins-nonnull.c: New test.
* gcc.dg/nonnull-4.c: New test.
2016-12-14 Nathan Sidwell <nathan@acm.org>
PR c++/78701

View File

@ -0,0 +1,239 @@
/* PR c/17308 - nonnull attribute not as useful as it could be
PR c/78673 - sprintf missing attribute nonnull on destination argument
{ dg-do "compile" }
{ dg-additional-options "-O2 -Wnonnull -ftrack-macro-expansion=0 -std=c99" } */
#define va_list __builtin_va_list
typedef struct FILE FILE;
char* null (void)
{
return 0;
}
void sink (int, ...);
#define T(arg) sink (0, arg)
#define bzero __builtin_bzero
#define memcpy __builtin_memcpy
#define memmove __builtin_memmove
#define mempcpy __builtin_mempcpy
#define memset __builtin_memset
void test_memfuncs (void *s, unsigned n)
{
/* Bzero is not declared attribute nonnull. */
bzero (null (), n);
T (memcpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (memcpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (memmove (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (memmove (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (mempcpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (mempcpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (memset (null (), 0, n)); /* { dg-warning "argument 1 null where non-null expected" } */
}
#undef memcpy
#undef memmove
#undef mempcpy
#undef memset
#define memcpy(d, s, n) __builtin___memcpy_chk (d, s, n, n)
#define memmove(d, s, n) __builtin___memmove_chk (d, s, n, n)
#define mempcpy(d, s, n) __builtin___mempcpy_chk (d, s, n, n)
#define memset(d, x, n) __builtin___memset_chk (d, x, n, n)
void test_memfuncs_chk (void *s, unsigned n)
{
T (memcpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (memcpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (memmove (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (memmove (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (mempcpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (mempcpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (memset (null (), 0, n)); /* { dg-warning "argument 1 null where non-null expected" } */
}
#define strcat __builtin_strcat
#define strchr __builtin_strchr
#define stpcpy __builtin_stpcpy
#define stpncpy __builtin_stpncpy
#define strcpy __builtin_strcpy
#define strncpy __builtin_strncpy
#define strlen __builtin_strlen
#define strncat __builtin_strncat
#define strstr __builtin_strstr
void test_strfuncs (char *s, unsigned n)
{
T (strcat (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strcat (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (strchr (null (), 'x')); /* { dg-warning "argument 1 null where non-null expected" } */
T (stpcpy (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (stpcpy (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (stpncpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (stpncpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (strcpy (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strcpy (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (strncpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strncpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (strlen (null ())); /* { dg-warning "argument 1 null where non-null expected" } */
T (strncat (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (strncat (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strstr (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strstr (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
}
#undef strcat
#undef stpcpy
#undef stpncpy
#undef strcpy
#undef strncpy
#undef strncat
#define strcat(d, s) __builtin___strcat_chk (d, s, n)
#define stpcpy(d, s) __builtin___stpcpy_chk (d, s, n)
#define stpncpy(d, s, n) __builtin___stpncpy_chk (d, s, n, n)
#define strcpy(d, s) __builtin___strcpy_chk (d, s, n)
#define strncpy(d, s, n) __builtin___strncpy_chk (d, s, n, n)
#define strncat(d, s, n) __builtin___strncat_chk (d, s, n, n)
void test_strfuncs_chk (char *s, unsigned n)
{
T (strcat (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strcat (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (strchr (null (), 'x')); /* { dg-warning "argument 1 null where non-null expected" } */
T (stpcpy (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (stpcpy (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (stpncpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (stpncpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (strcpy (null (), s)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strcpy (s, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (strncpy (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
T (strncpy (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (strncat (s, null (), n)); /* { dg-warning "argument 2 null where non-null expected" } */
T (strncat (null (), s, n)); /* { dg-warning "argument 1 null where non-null expected" } */
}
#define fprintf __builtin_fprintf
#define fprintf_unlocked __builtin_fprintf_unlocked
#define vfprintf __builtin_vfprintf
#define printf __builtin_printf
#define printf_unlocked __builtin_printf_unlocked
#define vprintf __builtin_vprintf
#define sprintf __builtin_sprintf
#define snprintf __builtin_snprintf
#define vsprintf __builtin_vsprintf
#define vsnprintf __builtin_vsnprintf
void test_stdio_funcs (FILE *f, char *d, unsigned n, va_list va)
{
T (fprintf (null (), "%i", 0)); /* { dg-warning "argument 1 null where non-null expected" } */
T (fprintf (f, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (fprintf_unlocked (null (), "%i", 0)); /* { dg-warning "argument 1 null where non-null expected" } */
T (fprintf_unlocked (f, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (vfprintf (null (), "%i", va));/* { dg-warning "argument 1 null where non-null expected" } */
T (vfprintf (f, null (), va)); /* { dg-warning "argument 2 null where non-null expected" } */
T (vprintf (null (), va)); /* { dg-warning "argument 1 null where non-null expected" } */
T (printf (null ())); /* { dg-warning "argument 1 null where non-null expected" } */
T (printf_unlocked (null ())); /* { dg-warning "argument 1 null where non-null expected" } */
T (vprintf (null (), va)); /* { dg-warning "argument 1 null where non-null expected" } */
T (sprintf (null (), "%i", 0)); /* { dg-warning "argument 1 null where non-null expected" } */
T (sprintf (d, null ())); /* { dg-warning "argument 2 null where non-null expected" } */
T (snprintf (null (), n, "%i", 0));
T (snprintf (d, n, null ())); /* { dg-warning "argument 3 null where non-null expected" } */
T (vsprintf (null (), "%i", va)); /* { dg-warning "argument 1 null where non-null expected" } */
T (vsprintf (d, null (), va)); /* { dg-warning "argument 2 null where non-null expected" } */
T (vsnprintf (null (), n, "%i", va));
T (vsnprintf (d, n, null (), va)); /* { dg-warning "argument 3 null where non-null expected" } */
}
#undef fprintf
#undef fprintf_unlocked
#undef vfprintf
#undef printf
#undef printf_unlocked
#undef vprintf
#undef sprintf
#undef snprintf
#undef vsprintf
#undef vsnprintf
#define fprintf(f, fmt, ...) \
__builtin___fprintf_chk (f, 0, fmt, __VA_ARGS__)
#define vfprintf(f, fmt, va) \
__builtin___vfprintf_chk (f, 0, fmt, va)
#define printf(fmt, ...) \
__builtin___printf_chk (0, fmt, __VA_ARGS__)
#define vprintf(fmt, va) \
__builtin___vprintf_chk (0, fmt, va)
#define sprintf(d, fmt, ... ) \
__builtin___sprintf_chk (d, 0, n, fmt, __VA_ARGS__)
#define snprintf(d, n, fmt, ...) \
__builtin___snprintf_chk (d, n, 0, n, fmt, __VA_ARGS__)
#define vsprintf(d, fmt, va) \
__builtin___vsprintf_chk (d, 0, n, fmt, va)
#define vsnprintf(d, n, fmt, va) \
__builtin___vsnprintf_chk (d, n, 0, n, fmt, va)
void test_stdio_funcs_chk (FILE *f, char *d, const char *fmt,
unsigned n, va_list va)
{
T (fprintf (null (), "%i", 0)); /* { dg-warning "argument 1 null where non-null expected" } */
T (fprintf (f, null (), 0)); /* { dg-warning "argument 3 null where non-null expected" } */
T (vfprintf (null (), "%i", va));/* { dg-warning "argument 1 null where non-null expected" } */
T (vfprintf (f, null (), va)); /* { dg-warning "argument 3 null where non-null expected" } */
T (vprintf (null (), va)); /* { dg-warning "argument 2 null where non-null expected" } */
T (printf (null (), 0)); /* { dg-warning "argument 2 null where non-null expected" } */
T (vprintf (null (), va)); /* { dg-warning "argument 2 null where non-null expected" } */
T (sprintf (null (), "%i", 0)); /* { dg-warning "argument 1 null where non-null expected" } */
T (sprintf (d, null (), 0)); /* { dg-warning "argument 4 null where non-null expected" } */
T (snprintf (null (), n, "%i", 0));
T (snprintf (d, n, null (), 0)); /* { dg-warning "argument 5 null where non-null expected" } */
T (vsprintf (null (), "%i", va)); /* { dg-warning "argument 1 null where non-null expected" } */
T (vsprintf (d, null (), va)); /* { dg-warning "argument 4 null where non-null expected" } */
T (vsnprintf (null (), n, "%i", va));
T (vsnprintf (d, n, null (), va)); /* { dg-warning "argument 5 null where non-null expected" } */
}

View File

@ -0,0 +1,79 @@
/* PR c/78673 - sprintf missing attribute nonnull on destination argument
Test to verify that calls to user-defined functions declared with
the "nonnull" function attribute are diagnosed. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wnonnull" } */
#define N(...) __attribute__ ((nonnull (__VA_ARGS__)))
void N (1) f1_1 (void*);
void N (1) f2_1 (void*, void*);
void N (1) N (2) f2_1_2 (void*, void*);
void N (1) N (3) f3_1_3 (void*, void*, void*);
void N (1, 2) N (4) g4_1_2_4 (void*, void*, void*, void*);
void N (1, 3) N (4) g4_1_3_4 (void*, void*, void*, void*);
void N (2, 3, 4) g4_2_3_4 (void*, void*, void*, void*);
void N () g4_all (void*, void*, void*, void*);
void N (1, 3, 5, 7, 11, 13)
g16_1_3_5_7_11_13 (void*, void*, void*, void*,
void*, void*, void*, void*,
void*, void*, void*, void*,
void*, void*, void*, void*);
void* null (void) { return 0; }
void test (void)
{
void *p0 = null ();
void *px = &px;
f1_1 (p0); /* { dg-warning "argument 1 null where non-null expected " } */
f1_1 (px);
f2_1 (p0, px); /* { dg-warning "argument 1 null" } */
f2_1 (px, p0);
f2_1 (p0, p0); /* { dg-warning "argument 1 null" } */
f2_1_2 (p0, px); /* { dg-warning "argument 1 null" } */
f2_1_2 (px, p0); /* { dg-warning "argument 2 null" } */
f2_1_2 (p0, p0); /* { dg-warning "argument 1 null" } */
/* { dg-warning "argument 2 null" "argument 2" { target *-*-* } .-1 } */
f3_1_3 (p0, px, px); /* { dg-warning "argument 1 null" } */
f3_1_3 (px, p0, px);
f3_1_3 (px, px, p0); /* { dg-warning "argument 3 null" } */
f3_1_3 (p0, p0, px); /* { dg-warning "argument 1 null" } */
f3_1_3 (px, p0, p0); /* { dg-warning "argument 3 null" } */
f3_1_3 (p0, p0, p0); /* { dg-warning "argument 1 null" } */
/* { dg-warning "argument 3 null" "argument 3" { target *-*-* } .-1 } */
g4_1_2_4 (p0, px, px, px); /* { dg-warning "argument 1 null" } */
g4_1_2_4 (px, p0, px, px); /* { dg-warning "argument 2 null" } */
g4_1_2_4 (px, px, p0, px);
g4_1_2_4 (px, px, px, p0); /* { dg-warning "argument 4 null" } */
g4_1_3_4 (p0, px, px, px); /* { dg-warning "argument 1 null" } */
g4_1_3_4 (px, p0, px, px);
g4_1_3_4 (px, px, p0, px); /* { dg-warning "argument 3 null" } */
g4_1_3_4 (px, px, px, p0); /* { dg-warning "argument 4 null" } */
g4_2_3_4 (p0, px, px, px);
g4_2_3_4 (px, p0, px, px); /* { dg-warning "argument 2 null" } */
g4_2_3_4 (px, px, p0, px); /* { dg-warning "argument 3 null" } */
g4_2_3_4 (px, px, px, p0); /* { dg-warning "argument 4 null" } */
g4_all (p0, px, px, px); /* { dg-warning "argument 1 null" } */
g4_all (px, p0, px, px); /* { dg-warning "argument 2 null" } */
g4_all (px, px, p0, px); /* { dg-warning "argument 3 null" } */
g4_all (px, px, px, p0); /* { dg-warning "argument 4 null" } */
g16_1_3_5_7_11_13 (px, px, px, px, px, px, px, px,
px, px, px, px, px, px, px, px);
g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, p0, p0); /* { dg-warning "argument 13 null" } */
}