From: Martin Sebor Date: Wed, 14 Dec 2016 17:23:16 +0000 (+0000) Subject: PR c/78673 - sprintf missing attribute nonnull on destination argument X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=474da67ef9ec3658e4da9deb5373353532b2a840;p=gcc.git PR c/78673 - sprintf missing attribute nonnull on destination argument PR c/78673 - sprintf missing attribute nonnull on destination argument PR c/17308 - nonnull attribute not as useful as it could be gcc/ChangeLog: PR c/17308 * builtin-attrs.def (ATTR_NONNULL_1_1, ATTR_NONNULL_1_2): Defined. (ATTR_NONNULL_1_3, ATTR_NONNULL_1_4, ATTR_NONNULL_1_5): Same. (ATTR_NOTHROW_NONNULL_1_1, ATTR_NOTHROW_NONNULL_1_2): Same. (ATTR_NOTHROW_NONNULL_1_3, ATTR_NOTHROW_NONNULL_1_4): Same. (ATTR_NOTHROW_NONNULL_1_5): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_1_2): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_2_0): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_2_3): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_3_0): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_3_4): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_4_0): Same. (ATTR_NONNULL_1_FORMAT_PRINTF_4_5): Same. * builtins.c (validate_arg): Add argument. Treat null pointers passed to nonnull arguments as invalid. (validate_arglist): Same. * builtins.def (fprintf, fprintf_unlocked): Add nonnull attribute. (printf, printf_unlocked, sprintf. vfprintf, vsprintf): Same. (__sprintf_chk, __vsprintf_chk, __fprintf_chk, __vfprintf_chk): Same. * calls.c (get_nonnull_ags, maybe_warn_null_arg): New functions. (initialize_argument_information): Diagnose null pointers passed to arguments declared nonnull. * calls.h (get_nonnull_args): Declared. gcc/c-family/ChangeLog: PR c/17308 * c-common.c (check_nonnull_arg): Disable when optimization is enabled. gcc/testsuite/ChangeLog: PR c/17308 * gcc.dg/builtins-nonnull.c: New test. * gcc.dg/nonnull-4.c: New test. From-SVN: r243661 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bfeb1c3a076..b52d9b41a28 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,29 @@ +2016-12-14 Martin Sebor + + 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 * config/rs6000/rs6000.c (rs6000_split_vec_extract_var): On ISA diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 1520d151007..22452d99c09 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 b056e1227ea..ca038cdcc7c 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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)); diff --git a/gcc/builtins.def b/gcc/builtins.def index 9cd24e8a89c..24b34e80cf1 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/ChangeLog b/gcc/c-family/ChangeLog index 547edadab3d..39a3582289f 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2016-12-14 Martin Sebor + + PR c/17308 + * c-common.c (check_nonnull_arg): Disable when optimization + is enabled. + 2016-12-12 Marek Polacek PR c++/78647 diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index c8e1f0da3c1..b690afb5e04 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 bc3cbd599fc..84664273fd4 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -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. diff --git a/gcc/calls.h b/gcc/calls.h index 3b0726345af..9d2084c1c81 100644 --- a/gcc/calls.h +++ b/gcc/calls.h @@ -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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 47a98ac1a01..64d7839c375 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-12-14 Martin Sebor + + PR c/17308 + * gcc.dg/builtins-nonnull.c: New test. + * gcc.dg/nonnull-4.c: New test. + 2016-12-14 Nathan Sidwell 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 index 00000000000..fa9eaf2327d --- /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" } */ +} diff --git a/gcc/testsuite/gcc.dg/nonnull-4.c b/gcc/testsuite/gcc.dg/nonnull-4.c new file mode 100644 index 00000000000..577a04c6185 --- /dev/null +++ b/gcc/testsuite/gcc.dg/nonnull-4.c @@ -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" } */ +}