This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] add missing attribute nonnull to stdio functions (PR 78673 and 17308)


The new -Wformat-length warning pass detects and diagnoses non-
constant null format strings.  This is in addition to (but not
in conflict with) -Wformat which is limited to detecting only
null constants.

A recently posted patch of mine also adds the detection of null
pointer arguments to the %s directive, and null destination
pointers:

  https://gcc.gnu.org/ml/gcc-patches/2016-12/msg00304.html

After I put that patch together and tested it I realized that
detecting null pointers is the business of the -Wnonnull warning
and would be better handled more generally for all functions
decorated with attribute nonnull.  I set out to look for a better
place to issue the non-constant null pointer format string (and
other sprintf-related) warnings.  In the process I discovered that
some of the sprintf functions (quite a few in fact) are missing
this attribute on some of their arguments.

The attached patch adds the nonnull attribute missing from some
of the stdio functions.  To achieve parity with -Wformat-length,
and to also resolve the ancient request in bug 17308 for more
useful nonnull warnings, the patch also adds the attribute nonnull
handling to the middle end so that all known null pointers can be
detected and diagnosed, not just constants.

To avoid duplicating warnings issued by the front and middle ends
I made the warning conditional on optimization in both (the front
end issues its diagnostics when not optimizing, the middle end
when optimizing).  I did this so as not to suppress warnings that
GCC issues only without optimization (e.g., for memcpy(0, 0, 0)).
Since these calls are eliminated I don't think thee warnings for
them are terribly important so if there's a preference for issuing
the warning just in the middle end I'm open to removing the front
end code.

Martin
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" } */
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]