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/78673
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/78673
PR c/17308
* c-common.c (check_nonnull_arg): Disable when optimization
is enabled.
gcc/testsuite/ChangeLog:
PR c/78673
PR c/17308
* gcc.dg/builtins-nonnull.c: New test.
* gcc.dg/nonnull-4.c: New test.
@@ -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)
@@ -145,7 +145,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);
@@ -1033,7 +1033,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
@@ -1048,9 +1048,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:
@@ -1062,23 +1067,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;
}
@@ -8622,15 +8635,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));
@@ -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. */
@@ -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);
}
@@ -1194,6 +1194,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.
@@ -1359,6 +1444,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
bitmap_obstack_release (NULL);
+ /* 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++)
{
@@ -1590,12 +1678,18 @@ 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. */
targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
type, argpos < n_named_args);
}
+
+ BITMAP_FREE (nonnullargs);
}
/* Update ARGS_SIZE to contain the total size for the argument block.
@@ -37,7 +37,7 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
tree, bool);
extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
tree, bool);
-
+extern bitmap get_nonnull_args (const_tree);
#endif // GCC_CALLS_H
new file mode 100644
@@ -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" } */
+}
new file mode 100644
@@ -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" } */
+}