From patchwork Thu Dec 8 03:24:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 87190 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp648064qgi; Wed, 7 Dec 2016 19:25:44 -0800 (PST) X-Received: by 10.99.211.21 with SMTP id b21mr81844359pgg.120.1481167544611; Wed, 07 Dec 2016 19:25:44 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id d189si26763451pga.29.2016.12.07.19.25.44 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 07 Dec 2016 19:25:44 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-443754-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-443754-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-443754-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 :subject:to:references:from:message-id:date:mime-version :in-reply-to:content-type; q=dns; s=default; b=CYBYdWviBMbgj1BzZ ksEf+dVjJ7fIeg0VIyi8Kve0HBwC81/KO/xgovTRE0GdpmWixXdWzp9UTC46qIVy B8HSk72ABdbQXcvUSUAXwAHFu2h/j0EmxYmGWt6UbTblejuYLnzseF6eTSNMT9ow UJ/04Dr8g5Lq32iPVCk1P53fks= 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 :subject:to:references:from:message-id:date:mime-version :in-reply-to:content-type; s=default; bh=dzLT+Ot0JiYhxpmF1+0xqfY oyaM=; b=IXtmQGbu88tAErh4D/OhyUq+IpR/mVKSyZDbYALlaOtPeo0DRcaCic6 JoCsmj5Pz9dRwBmQwbBz3uF8JTYZJ6MdCmI5xbEfthJehcO+3muUXKim1vwRr0aV odhCPOC1bEaXBq1M5QAu5xIB27lAyNIRqoOhxZMynH62YsagL04U= Received: (qmail 118546 invoked by alias); 8 Dec 2016 03:25:20 -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 117913 invoked by uid 89); 8 Dec 2016 03:24:43 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.5 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 spammy=__attribute, sk:ATTRIBU, INTEGER_TYPE, integer_type X-HELO: mail-qt0-f181.google.com Received: from mail-qt0-f181.google.com (HELO mail-qt0-f181.google.com) (209.85.216.181) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 08 Dec 2016 03:24:38 +0000 Received: by mail-qt0-f181.google.com with SMTP id c47so400894323qtc.2 for ; Wed, 07 Dec 2016 19:24:37 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to; bh=RW2ySTxbSyKm3JLT2rvZ8VWDeqtphPjmTUrHP+bY6j8=; b=k2I6ssBQKAbksCMUURvLC9H6KVGbwch8WmbzsRPrwYMPt4TlFajhBRjqYwynV09ND7 mS0+D4SrvNxmRPDVn+2bymzpHdRGeNCsBKuM4sAPBoLXDe+1ZmRioVx0wonCbKzIGsim Ku2WOt4fcqNt7fQBftQp2VOIISxuOmiunV/zGnIGo7ulMt8bpociqkmL5wmFf/B1o8T4 DIQZDS+CzQ3L3UZrFcwxTvr0SHhcP4fj/82+jU+LGfAT8Vd/Og6xh8CVpKBXGgf2SW9k 07EFpd6st//62iBRAip1o3ysZfkJeEXcWgLi7nSwU2q3wpFE2mqZumgFOfR2Fy+5v3ch 0RSw== X-Gm-Message-State: AKaTC01gY5gfIp8YNdHwOdPnC/6H9YAyuGaw3wwKJK7c3puEPpGsGvHRI7tcMxju4QJW9g== X-Received: by 10.200.36.148 with SMTP id s20mr61049748qts.275.1481167476450; Wed, 07 Dec 2016 19:24:36 -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 w34sm16426371qtw.10.2016.12.07.19.24.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 07 Dec 2016 19:24:35 -0800 (PST) Subject: Re: [PATCH] add missing attribute nonnull to stdio functions (PR 78673 and 17308) To: Jeff Law , Gcc Patch List References: <73289020-7e3e-e5fc-2cdc-12115e11fe31@redhat.com> From: Martin Sebor Message-ID: <427c4e0d-6437-98ab-a7ee-f0e3647192bb@gmail.com> Date: Wed, 7 Dec 2016 20:24:33 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 In-Reply-To: <73289020-7e3e-e5fc-2cdc-12115e11fe31@redhat.com> X-IsSubscribed: yes >> +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; >> +} > I'm not entirely sure you're using lookup_attribute properly here. The > docs for that function make me believe you need to call back into > lookup_attribute with the TREE_CHAIN of the previous return value to > find the next occurrence. You're right! Good chatch! I missed that there are two ways to represent the same thing. For example, these two declarations void __attribute ((nonnull (1, 2))) f (void); void __attribute ((nonnull (1))) __attribute ((nonnull (2))) f (void); apply to the same arguments but each is represented differently, as is this one: void __attribute ((nonnull)) f (void); The builtins use the first form and I didn't have tests for user- defined attributes in the second form. I've fixed that in the attached updated patch (and added more tests). It does seem, though, that it would be better to represent these declarations canonically. It would simplify the processing and avoid bugs. Thanks 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. * gcc.dg/nonnull-4.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..284e798 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1194,6 +1194,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. @@ -1359,6 +1444,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 +1678,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" } */ +} diff --git a/gcc/testsuite/gcc.dg/nonnull-4.c b/gcc/testsuite/gcc.dg/nonnull-4.c new file mode 100644 index 0000000..577a04c --- /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" } */ +}