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.

diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def
index 1520d15..c55523e 100644
--- a/gcc/builtin-attrs.def
+++ b/gcc/builtin-attrs.def
@@ -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)
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 1316c27..9777081 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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));
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 9cd24e8..24b34e8 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -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.  */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 0749361..ac624d8 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -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);
 }
diff --git a/gcc/calls.c b/gcc/calls.c
index 21385ce..235dec0 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1194,6 +1194,76 @@ 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.  */
+
+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;
+
+  attrs = lookup_attribute ("nonnull", attrs);
+  if (!attrs)
+    return NULL;
+
+  /* Return an empty but non-null bitmap as an indication that all
+     of the function's arguments must be non-null.  */
+  bitmap argmap = BITMAP_ALLOC (NULL);
+  if (!TREE_VALUE (attrs))
+    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 +1429,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 +1663,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.
diff --git a/gcc/calls.h b/gcc/calls.h
index e144156..5edc1db 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -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
diff --git a/gcc/testsuite/gcc.dg/builtins-nonnull.c b/gcc/testsuite/gcc.dg/builtins-nonnull.c
new file mode 100644
index 0000000..fa9eaf2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtins-nonnull.c
@@ -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" } */
+}
