PR c/78673 - sprintf missing attribute nonnull on destination argument
authorMartin Sebor <msebor@redhat.com>
Wed, 14 Dec 2016 17:23:16 +0000 (17:23 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 14 Dec 2016 17:23:16 +0000 (10:23 -0700)
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

gcc/ChangeLog
gcc/builtin-attrs.def
gcc/builtins.c
gcc/builtins.def
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/calls.c
gcc/calls.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/builtins-nonnull.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-4.c [new file with mode: 0644]

index bfeb1c3a076304d3b5fbe078814b1991a1ee557e..b52d9b41a28d5b53c3824b62143bd5818cbc7da1 100644 (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
index 1520d151007f6be15c2069a257556721c0edb5cf..22452d99c09f3fa9323d69cc13cb345bfc414618 100644 (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)
index b056e1227ea505f26ac7728d704264a305514868..ca038cdcc7cc44d8aa30e3ec67114f45b9ed5b22 100644 (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));
index 9cd24e8a89ce48dd9e299f529d7176fb10ec0257..24b34e80cf1114eafd07b6cd0c7f05d30aae1999 100644 (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.  */
index 547edadab3d6d5095f635c42bfa6e82aa54b534e..39a3582289fe8f2822c067cd6503f1003bf13305 100644 (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
index c8e1f0da3c100e57466cdb9643dc1348439ae45d..b690afb5e04eaaf576e582d522c17fb3c1a9b67b 100644 (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);
 }
index bc3cbd599fc321a5422a2a7b214952eb14aded71..84664273fd4f55f8bf6c9157230c7ec56b40fbe6 100644 (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.
index 3b0726345afb98430d380531b1c8bb757c67f61d..9d2084c1c816c42c67cd552d0a861c1b9deddfd4 100644 (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
index 47a98ac1a017d87a30b2ad9c14598244dfb66020..64d7839c3750929f5665afcd85304f540ee8c324 100644 (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
diff --git a/gcc/testsuite/gcc.dg/builtins-nonnull.c b/gcc/testsuite/gcc.dg/builtins-nonnull.c
new file mode 100644 (file)
index 0000000..fa9eaf2
--- /dev/null
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/nonnull-4.c b/gcc/testsuite/gcc.dg/nonnull-4.c
new file mode 100644 (file)
index 0000000..577a04c
--- /dev/null
@@ -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" } */
+}