From patchwork Mon Dec 5 22:56:51 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 86648 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp1735576qgi; Mon, 5 Dec 2016 14:57:23 -0800 (PST) X-Received: by 10.84.136.1 with SMTP id 1mr130031232plk.152.1480978643835; Mon, 05 Dec 2016 14:57:23 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id o1si16237548pge.141.2016.12.05.14.57.23 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 05 Dec 2016 14:57:23 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-443536-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org; spf=pass (google.com: domain of gcc-patches-return-443536-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-443536-patch=linaro.org@gcc.gnu.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :subject:to:message-id:date:mime-version:content-type; q=dns; s= default; b=mGznduh1epxYU1V0Bj/PWvXu9c1sVyMGooL/VcZAknxEt6CF3RMm/ D+xkQZX36Wv/c241XKDcjTiPpLXZ6SCcHc2z5N+2uTK3FbJ8c1CtWaZ5YelTL73m 5eh5d2XpwmvU81ECCuLVS79HjbpLC3xYf9GMxJhe0uC/FN86hEOgMs= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :subject:to:message-id:date:mime-version:content-type; s= default; bh=VBSntVV5rqC5RdMl/7evT7pniNs=; b=r7uvMGKJ5j3r0zA9/agK R7p2cZn2sB5rND+dGE/GAnK9XtBM8SA8FfojyGFEvntvkavyqZ2Sz9bbiN/i6X9k g6hoRQP3j94g2JCTSjsokVAPOH7vrziKDRvpV0e8lNiYpD3x+LbpJ4Ul8Epu670j geu8OiPv0rwKIEZGqTC2FiU= Received: (qmail 124807 invoked by alias); 5 Dec 2016 22:57:07 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 124796 invoked by uid 89); 5 Dec 2016 22:57:06 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.0 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=ordinary, memmove, Category, Profiling X-HELO: mail-qt0-f182.google.com Received: from mail-qt0-f182.google.com (HELO mail-qt0-f182.google.com) (209.85.216.182) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 05 Dec 2016 22:56:56 +0000 Received: by mail-qt0-f182.google.com with SMTP id w33so329572914qtc.3 for ; Mon, 05 Dec 2016 14:56:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:subject:to:message-id:date:user-agent :mime-version; bh=JGFhoGl7zDWtm5aJcQ6kQTbPVZrNTpCkBriPL7aOpPI=; b=hxrRvNNk68vQGzYeRWjQTLsSCYq4pzIkinbNgQrr20T96PialxIlr9R4sS78VUDD5U I3LZmAuRBInLnr6p4fp23FKuMk5ugNdWV/YOlzBL61MuJWKspUa81/rR8Sb5dbxgAbWQ nXUlgOQpd4wqAqinhLMh2NGDgs5un75tE2nA5x2iJxsvmZHo4X4Mek4h9Ske1WkSCTXP Ef/udJvIzrlwoV7DA9NOWE2ebVOpiK+aPUrgoMOjYz/xka0Ff56vF9O9ScsO1ny3Aa0I IZYCD9Idygey6MeLtmtYGmWNFZMpHss4sLW/yc59VLFl8cor5k901FPI0G5A3+1Q4F6d ej5g== X-Gm-Message-State: AKaTC02eULzycZKeQZobGxDE+gqbU9Gdsqo5rDD0Rr4Gv5eRKkT5vA73Aog4tLzoWMOrzQ== X-Received: by 10.200.36.125 with SMTP id d58mr50910199qtd.126.1480978614180; Mon, 05 Dec 2016 14:56:54 -0800 (PST) Received: from [192.168.0.26] (97-122-174-22.hlrn.qwest.net. [97.122.174.22]) by smtp.gmail.com with ESMTPSA id w8sm10522150qta.25.2016.12.05.14.56.53 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 05 Dec 2016 14:56:53 -0800 (PST) From: Martin Sebor Subject: [PATCH] add missing attribute nonnull to stdio functions (PR 78673 and 17308) To: Gcc Patch List Message-ID: Date: Mon, 5 Dec 2016 15:56:51 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 X-IsSubscribed: yes 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" } */ +}