From patchwork Wed Nov 16 17:19:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 82577 Delivered-To: patch@linaro.org Received: by 10.140.97.165 with SMTP id m34csp274362qge; Wed, 16 Nov 2016 09:20:27 -0800 (PST) X-Received: by 10.98.90.132 with SMTP id o126mr6038542pfb.41.1479316827897; Wed, 16 Nov 2016 09:20:27 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id e11si23852670pgp.204.2016.11.16.09.20.27 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 16 Nov 2016 09:20:27 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-441686-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-441686-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-441686-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:references:message-id:date:mime-version:in-reply-to :content-type; q=dns; s=default; b=JV4tMtGaYzwJiH/i174Pd8QJsy1VN uE0fRAgqhegwk4dE61WcharkIjV8asP2Q9Yk2K5DJOoDdPF83c73BRzXeYTDJ/RF OO0EOJOlNHPRFsqRZPxuUu0xHYW+b7Tu6ucOMTwpFMkXStNv24BvYp7ma6yButjt 5MFOC9RpujHCZQ= 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:references:message-id:date:mime-version:in-reply-to :content-type; s=default; bh=v6xZO85FxMsZBiGoisj1LHrQs4A=; b=haL M/FCEQ1/AXhEJ05l4oN+buvRnnP+x1G+c7ubp8MrZgbl6pQM3O/cyE/MPrAdU1dj p6NQ5G91nL6HwrGEtcXXsj+WF3S3X8/hwdZVQOKlQb6r/Np9F7uram1VqKVDhc8a l/+PoRMYClsRFkwzUpQNxp8TYGzVYb0t02xQTsu4= Received: (qmail 1548 invoked by alias); 16 Nov 2016 17:20:06 -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 1522 invoked by uid 89); 16 Nov 2016 17:20:05 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=1.0 required=5.0 tests=AWL, BAYES_50, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=no version=3.3.2 spammy=ulong_max, ULONG_MAX, XALLOCAVEC, xallocavec X-HELO: mail-qk0-f194.google.com Received: from mail-qk0-f194.google.com (HELO mail-qk0-f194.google.com) (209.85.220.194) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 16 Nov 2016 17:19:58 +0000 Received: by mail-qk0-f194.google.com with SMTP id 124so22200009qkh.1 for ; Wed, 16 Nov 2016 09:19:57 -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:references:message-id:date :user-agent:mime-version:in-reply-to; bh=bYwkLZ2fSbKAI5lo2Q6kM9QrLqnKxta9Ajvsvso+rXQ=; b=L7UuSk5FF3H3REo15DSe1bQXjGwkYM+yaD5gfze0uUs9wbgzeU/n9EeNLgDt7gdQLC Qv0hbhJtUC04hsaKZWUif+Rop45RJJwgHaBUDeRKfiBEwg/E42b8yPx6B+gh6nDDIgIh R+WDjWjzY6NZIFuK7PLajpJhvKNJVt4q6Rw98Kff7B9K4EN66bWAK56bVpWKfKpH5/mB TUQWvNTRYbLhPZPwjCcoRDdF97nZnIR/EJpYrqFylkOWb31bdRXSCLiFpwNDAMdXXM2r 6OruaT3vIORjcLpuYQr4SvuVbEkSF4IY+Rh2029CSrND+DmCulgPQ1m0plTkXEz5rGl6 dsdA== X-Gm-Message-State: AKaTC016/3TbwbEk00TNaEg5G/69blQQWHNT3mfpiaUXnYgOambvAGtyp5SQB+z2XmQqPg== X-Received: by 10.55.41.159 with SMTP id p31mr4402556qkp.212.1479316796173; Wed, 16 Nov 2016 09:19:56 -0800 (PST) Received: from [192.168.0.26] (75-166-206-79.hlrn.qwest.net. [75.166.206.79]) by smtp.gmail.com with ESMTPSA id x123sm18085950qkx.46.2016.11.16.09.19.54 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 16 Nov 2016 09:19:55 -0800 (PST) From: Martin Sebor Subject: Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284) To: Gcc Patch List References: <204efcec-649f-34a1-22a1-161d1d98ea95@gmail.com> Message-ID: Date: Wed, 16 Nov 2016 10:19:53 -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: <204efcec-649f-34a1-22a1-161d1d98ea95@gmail.com> X-IsSubscribed: yes Attached is an updated version of the patch that also adds attribute alloc_size to the standard allocation built-ins (aligned_alloc, alloca, malloc, calloc, and realloc) and handles alloca. Besides that, I've renamed the option to -Walloc-size-larger-than to make it less similar to -Walloca-larger-than. It think the new name works because the option works with the alloc_size attribute. Other suggestions are of course welcome. I've left the alloc_max_size function in place until I receive some feedback on it. I've regression-tested the patch on x86_64 with a few issues. The biggest is that the -Walloc-zero option enabled by -Wextra causes a number of errors during bootstrap due to invoking the XALLOCAVEC macro with a zero argument. The errors look valid to me (and I got past them by temporarily changing the XALLOCAVEC macro to always allocate at least one byte) but I haven't fixed the errors yet. I'll post a separate patch for those. The other open issue is that the new warning duplicates a small subset of the -Walloca-larger-than warnings. I expect removing the duplicates to be straightforward. I post this updated patch for review while I work on the remaining issues. Martin On 11/13/2016 08:19 PM, Martin Sebor wrote: > Bug 77531 requests a new warning for calls to allocation functions > (those declared with attribute alloc_size(X, Y)) that overflow the > computation X * Z of the size of the allocated object. > > Bug 78284 suggests that detecting and diagnosing other common errors > in calls to allocation functions, such as allocating more space than > SIZE_MAX / 2 bytes, would help prevent subsequent buffer overflows. > > The attached patch adds two new warning options, -Walloc-zero and > -Walloc-larger-than=bytes that implement these two enhancements. > The patch is not 100% finished because, as it turns out, the GCC > allocation built-ins (malloc et al.) do not make use of the > attribute and so don't benefit from the warnings. The tests are > also incomplete, and there's at least one bug in the implementation > I know about. > > I'm posting the patch while stage 1 is still open and to give > a heads up on it and to get early feedback. I expect completing > it will be straightforward. > > Martin > > PS The alloc_max_size function added in the patch handles sizes > specified using suffixes like KB, MB, etc. I added that to make > it possible to specify sizes in excess of the maximum of INT_MAX > that (AFAIK) options that take integer arguments handle out of > the box. It only belatedly occurred to me that the suffixes > are unnecessary if the option argument is handled using strtoull. > I can remove the suffix (as I suspect it will raise objections) > but I think that a general solution along these lines would be > useful to let users specify large byte sizes in other options > as well (such -Walloca-larger-than, -Wvla-larger-then). Are > there any suggestions or preferences? PR c/77531 - __attribute__((alloc_size(1,2))) could also warn on multiplication overflow PR c/78284 - warn on malloc with very large arguments include/ChangeLog: * libiberty.h (XALLOCAVEC): Make sure alloca argument is non-zero. gcc/c-family/ChangeLog: PR c/77531 PR c/78284 * c.opt (-Walloc-zero, -Walloc-larger-than): New options. gcc/ChangeLog: PR c/77531 PR c/78284 * builtin-attrs.def (ATTR_ALLOC_SIZE): New identifier tree. (ATTR_MALLOC_SIZE_1_NOTHROW_LIST): New attribute list. (ATTR_MALLOC_SIZE_1_NOTHROW_LEAF_LIST): Same. (ATTR_MALLOC_SIZE_1_2_NOTHROW_LEAF_LIST): Same. (ATTR_ALLOC_SIZE_2_NOTHROW_LEAF_LIST): Same. * builtins.c (expand_builtin_alloca): Call maybe_warn_alloc_args_overflow. * builtins.def (akigned_alloc, alloca, calloc, malloc, realloc): Add attribute alloc_size. * calls.h (maybe_warn_alloc_args_overflow): Declare. * calls.c (alloc_max_size): New function. (maybe_warn_alloc_args_overflow): Define. (initialize_argument_information): Diagnose overflow in functions declared with attaribute alloc_size. * doc/invoke.texi (Warning Options): Document -Walloc-zero and -Walloc-larger-than. gcc/testsuite/ChangeLog: PR c/77531 PR c/78284 * gcc.dg/attr-alloc_size-3.c: New test. * gcc.dg/attr-alloc_size-4.c: New test. * gcc.dg/attr-alloc_size-5.c: New test. * gcc.dg/attr-alloc_size-6.c: New test. * gcc.dg/attr-alloc_size-7.c: New test. diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 8dc59c9..2a58b31 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -83,6 +83,7 @@ DEF_LIST_INT_INT (5,6) #undef DEF_LIST_INT_INT /* Construct trees for identifiers. */ +DEF_ATTR_IDENT (ATTR_ALLOC_SIZE, "alloc_size") DEF_ATTR_IDENT (ATTR_COLD, "cold") DEF_ATTR_IDENT (ATTR_CONST, "const") DEF_ATTR_IDENT (ATTR_FORMAT, "format") @@ -150,6 +151,23 @@ DEF_ATTR_TREE_LIST (ATTR_SENTINEL_NOTHROW_LEAF_LIST, ATTR_SENTINEL, \ DEF_ATTR_TREE_LIST (ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST, ATTR_CONST,\ ATTR_NULL, ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +/* Allocation functions like alloca and malloc whose first argument + specifies the size of the allocated object. */ +DEF_ATTR_TREE_LIST (ATTR_MALLOC_SIZE_1_NOTHROW_LIST, ATTR_ALLOC_SIZE, \ + ATTR_LIST_1, ATTR_NOTHROW_LIST) +DEF_ATTR_TREE_LIST (ATTR_MALLOC_SIZE_1_NOTHROW_LEAF_LIST, ATTR_ALLOC_SIZE, \ + ATTR_LIST_1, ATTR_MALLOC_NOTHROW_LEAF_LIST) + +/* Allocation functions like calloc the product of whose first two arguments + specifies the size of the allocated object. */ +DEF_ATTR_TREE_LIST (ATTR_MALLOC_SIZE_1_2_NOTHROW_LEAF_LIST, ATTR_ALLOC_SIZE, \ + ATTR_LIST_1_2, ATTR_MALLOC_NOTHROW_LEAF_LIST) + +/* Allocation functions like realloc whose second argument specifies + the size of the allocated object. */ +DEF_ATTR_TREE_LIST (ATTR_ALLOC_SIZE_2_NOTHROW_LEAF_LIST, ATTR_ALLOC_SIZE, \ + ATTR_LIST_2, ATTR_NOTHROW_LEAF_LIST) + /* Functions whose pointer parameter(s) are all nonnull. */ DEF_ATTR_TREE_LIST (ATTR_NONNULL_LIST, ATTR_NONNULL, ATTR_NULL, ATTR_NULL) /* Functions whose first parameter is a nonnull pointer. */ diff --git a/gcc/builtins.c b/gcc/builtins.c index 3ac2d44..2637ae6 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -4327,12 +4327,12 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate) { rtx op0; rtx result; - bool valid_arglist; unsigned int align; - bool alloca_with_align = (DECL_FUNCTION_CODE (get_callee_fndecl (exp)) + tree fndecl = get_callee_fndecl (exp); + bool alloca_with_align = (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN); - valid_arglist + bool valid_arglist = (alloca_with_align ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE) : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE)); @@ -4340,6 +4340,10 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate) if (!valid_arglist) return NULL_RTX; + tree args[] = { CALL_EXPR_ARG (exp, 0), NULL_TREE }; + int idx[] = { 0, -1 }; + maybe_warn_alloc_args_overflow (fndecl, exp, args, idx); + /* Compute the argument. */ op0 = expand_normal (CALL_EXPR_ARG (exp, 0)); diff --git a/gcc/builtins.def b/gcc/builtins.def index 219feeb..1b31909 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -284,7 +284,7 @@ DEF_C99_BUILTIN (BUILT_IN_ACOSH, "acosh", BT_FN_DOUBLE_DOUBLE, ATTR_MATHF DEF_C99_BUILTIN (BUILT_IN_ACOSHF, "acoshf", BT_FN_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO) DEF_C99_BUILTIN (BUILT_IN_ACOSHL, "acoshl", BT_FN_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO) DEF_C99_C90RES_BUILTIN (BUILT_IN_ACOSL, "acosl", BT_FN_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO) -DEF_C11_BUILTIN (BUILT_IN_ALIGNED_ALLOC, "aligned_alloc", BT_FN_PTR_SIZE_SIZE, ATTR_MALLOC_NOTHROW_LIST) +DEF_C11_BUILTIN (BUILT_IN_ALIGNED_ALLOC, "aligned_alloc", BT_FN_PTR_SIZE_SIZE, ATTR_MALLOC_SIZE_1_NOTHROW_LIST) DEF_LIB_BUILTIN (BUILT_IN_ASIN, "asin", BT_FN_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO) DEF_C99_C90RES_BUILTIN (BUILT_IN_ASINF, "asinf", BT_FN_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO) DEF_C99_BUILTIN (BUILT_IN_ASINH, "asinh", BT_FN_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING) @@ -764,7 +764,7 @@ DEF_GCC_BUILTIN (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST) DEF_LIB_BUILTIN (BUILT_IN_ABS, "abs", BT_FN_INT_INT, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_AGGREGATE_INCOMING_ADDRESS, "aggregate_incoming_address", BT_FN_PTR_VAR, ATTR_LEAF_LIST) -DEF_EXT_LIB_BUILTIN (BUILT_IN_ALLOCA, "alloca", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_ALLOCA, "alloca", BT_FN_PTR_SIZE, ATTR_MALLOC_SIZE_1_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_APPLY, "apply", BT_FN_PTR_PTR_FN_VOID_VAR_PTR_SIZE, ATTR_NULL) DEF_GCC_BUILTIN (BUILT_IN_APPLY_ARGS, "apply_args", BT_FN_PTR_VAR, ATTR_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_BSWAP16, "bswap16", BT_FN_UINT16_UINT16, ATTR_CONST_NOTHROW_LEAF_LIST) @@ -772,7 +772,7 @@ DEF_GCC_BUILTIN (BUILT_IN_BSWAP32, "bswap32", BT_FN_UINT32_UINT32, ATTR_C DEF_GCC_BUILTIN (BUILT_IN_BSWAP64, "bswap64", BT_FN_UINT64_UINT64, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_CLEAR_CACHE, "__clear_cache", BT_FN_VOID_PTR_PTR, ATTR_NOTHROW_LEAF_LIST) /* [trans-mem]: Adjust BUILT_IN_TM_CALLOC if BUILT_IN_CALLOC is changed. */ -DEF_LIB_BUILTIN (BUILT_IN_CALLOC, "calloc", BT_FN_PTR_SIZE_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST) +DEF_LIB_BUILTIN (BUILT_IN_CALLOC, "calloc", BT_FN_PTR_SIZE_SIZE, ATTR_MALLOC_SIZE_1_2_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_CLASSIFY_TYPE, "classify_type", BT_FN_INT_VAR, ATTR_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_CLZ, "clz", BT_FN_INT_UINT, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_CLZIMAX, "clzimax", BT_FN_INT_UINTMAX, ATTR_CONST_NOTHROW_LEAF_LIST) @@ -848,7 +848,7 @@ DEF_LIB_BUILTIN (BUILT_IN_LABS, "labs", BT_FN_LONG_LONG, ATTR_CONST_NOTHR DEF_C99_BUILTIN (BUILT_IN_LLABS, "llabs", BT_FN_LONGLONG_LONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_LONGJMP, "longjmp", BT_FN_VOID_PTR_INT, ATTR_NORETURN_NOTHROW_LIST) /* [trans-mem]: Adjust BUILT_IN_TM_MALLOC if BUILT_IN_MALLOC is changed. */ -DEF_LIB_BUILTIN (BUILT_IN_MALLOC, "malloc", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST) +DEF_LIB_BUILTIN (BUILT_IN_MALLOC, "malloc", BT_FN_PTR_SIZE, ATTR_MALLOC_SIZE_1_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_NEXT_ARG, "next_arg", BT_FN_PTR_VAR, ATTR_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_PARITY, "parity", BT_FN_INT_UINT, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_PARITYIMAX, "parityimax", BT_FN_INT_UINTMAX, ATTR_CONST_NOTHROW_LEAF_LIST) @@ -860,7 +860,7 @@ DEF_GCC_BUILTIN (BUILT_IN_POPCOUNTL, "popcountl", BT_FN_INT_ULONG, ATTR_C DEF_GCC_BUILTIN (BUILT_IN_POPCOUNTLL, "popcountll", BT_FN_INT_ULONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_POSIX_MEMALIGN, "posix_memalign", BT_FN_INT_PTRPTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) DEF_GCC_BUILTIN (BUILT_IN_PREFETCH, "prefetch", BT_FN_VOID_CONST_PTR_VAR, ATTR_NOVOPS_LEAF_LIST) -DEF_LIB_BUILTIN (BUILT_IN_REALLOC, "realloc", BT_FN_PTR_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST) +DEF_LIB_BUILTIN (BUILT_IN_REALLOC, "realloc", BT_FN_PTR_PTR_SIZE, ATTR_ALLOC_SIZE_2_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_RETURN, "return", BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_RETURN_ADDRESS, "return_address", BT_FN_PTR_UINT, ATTR_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_SAVEREGS, "saveregs", BT_FN_PTR_VAR, ATTR_NULL) diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 213353b..72c1e14 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -299,6 +299,15 @@ Walloca C ObjC C++ ObjC++ Var(warn_alloca) Warning Warn on any use of alloca. +Walloc-larger-than= +C ObjC C++ ObjC++ Var(warn_alloc_limit) Warning Joined +-Walloc-larger-than= Warn for calls to allocation functions that attempt +to allocate objects larger than the specified number of bytes. + +Walloc-zero +C ObjC C++ ObjC++ Var(warn_alloc_zero) Warning EnabledBy(Wextra) +-Walloc-zero Warn for calls to allocation functions that specify zero bytes. + Walloca-larger-than= C ObjC C++ ObjC++ Var(warn_alloca_limit) Warning Joined RejectNegative UInteger -Walloca-larger-than= Warn on unbounded uses of diff --git a/gcc/calls.c b/gcc/calls.c index c916e07..6a5d339 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -48,8 +48,10 @@ along with GCC; see the file COPYING3. If not see #include "dbgcnt.h" #include "rtl-iter.h" #include "tree-chkp.h" +#include "tree-vrp.h" +#include "tree-ssanames.h" #include "rtl-chkp.h" - +#include "intl.h" /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */ #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) @@ -1181,6 +1183,239 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals) } } +static tree alloc_object_size_limit; + +/* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-larger-than=limit + setting if the option is specified, or to the maximum object size if it + is not. Return the initialized value. */ + +static tree +alloc_max_size (void) +{ + if (!alloc_object_size_limit) + { + alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype); + + unsigned HOST_WIDE_INT unit = 1; + + char *end; + errno = 0; + unsigned HOST_WIDE_INT limit + = warn_alloc_limit ? strtoull (warn_alloc_limit, &end, 10) : 0; + + if (limit && !errno) + { + if (end && *end) + { + /* Numeric option arguments are at most INT_MAX. Make it + possible to specify a larger value by accepting common + suffixes. */ + if (!strcmp (end, "kB")) + unit = 1000; + else if (!strcasecmp (end, "KiB") || strcmp (end, "KB")) + unit = 1024; + else if (!strcmp (end, "MB")) + unit = 1000LU * 1000; + else if (!strcasecmp (end, "MiB")) + unit = 1024LU * 1024; + else if (!strcasecmp (end, "GB")) + unit = 1000LU * 1000 * 1000; + else if (!strcasecmp (end, "GiB")) + unit = 1024LU * 1024 * 1024; + else if (!strcasecmp (end, "TB")) + unit = 1000LU * 1000 * 1000 * 1000; + else if (!strcasecmp (end, "TiB")) + unit = 1024LU * 1024 * 1024 * 1024; + else if (!strcasecmp (end, "PB")) + unit = 1000LU * 1000 * 1000 * 1000 * 1000; + else if (!strcasecmp (end, "PiB")) + unit = 1024LU * 1024 * 1024 * 1024 * 1024; + else if (!strcasecmp (end, "EB")) + unit = 1000LU * 1000 * 1000 * 1000 * 1000 * 1000; + else if (!strcasecmp (end, "EiB")) + unit = 1024LU * 1024 * 1024 * 1024 * 1024 * 1024; + else + unit = 0; + } + + if (unit) + alloc_object_size_limit = build_int_cst (ssizetype, limit * unit); + } + } + return alloc_object_size_limit; +} + +/* Diagnose a call EXP to function FN decorated with attribute alloc_size + whose argument numbers given by IDX with values given by ARGS exceed + the maximum object size or cause an unsigned oveflow (wrapping) when + multiplied. When ARGS[0] is null the function does nothing. ARGS[1] + may be null for functions like malloc, and non-null for those like + calloc that are decorated with a two-argument attribute alloc_size. */ + +void +maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) +{ + /* The range each of the (up to) two arguments is known to be in. */ + tree argrange[2][2] = { { NULL_TREE, NULL_TREE }, { NULL_TREE, NULL_TREE } }; + + /* Maximum object size set by -Walloc-larger-than= or SIZE_MAX / 2. */ + tree maxobjsize = alloc_max_size (); + + location_t loc = tree_nonartificial_location (exp); // EXPR_LOCATION (exp); + + bool warned = false; + + /* Validate each argument individually. */ + for (unsigned i = 0; i != 2 && args[i]; ++i) + { + if (TREE_CODE (args[i]) == INTEGER_CST) + { + argrange[i][0] = args[i]; + argrange[i][1] = args[i]; + + if (tree_int_cst_lt (args[i], integer_zero_node)) + { + warned = warning_at (loc, OPT_Walloc_larger_than_, + "argument %i value %qE is negative", + idx[i] + 1, args[i]); + } + else if (integer_zerop (args[i])) + { + warned = warning_at (loc, OPT_Walloc_zero, + "argument %i value is zero", + idx[i] + 1); + } + else if (tree_int_cst_lt (maxobjsize, args[i])) + { + /* G++ emits calls to ::operator new[](SIZE_MAX) in C++98 + mode and with -fno-exceptions as a way to indicate array + size overflow. There's no good way to detect C++98 here + so avoid diagnosing these calls for all C++ modes. */ + if (i == 0 + && !args[1] + && lang_GNU_CXX () + && DECL_IS_OPERATOR_NEW (fn) + && integer_all_onesp (args[i])) + continue; + + warned = warning_at (loc, OPT_Walloc_larger_than_, + "argument %i value %qE exceeds " + "maximum object size %E", + idx[i] + 1, args[i], maxobjsize); + } + } + else if (TREE_CODE (args[i]) == SSA_NAME) + { + tree type = TREE_TYPE (args[i]); + + wide_int min, max; + value_range_type range_type = get_range_info (args[i], &min, &max); + if (range_type == VR_RANGE) + { + argrange[i][0] = wide_int_to_tree (type, min); + argrange[i][1] = wide_int_to_tree (type, max); + } + else if (range_type == VR_ANTI_RANGE) + { + argrange[i][0] = wide_int_to_tree (type, max + 1); + argrange[i][1] = wide_int_to_tree (type, min - 1); + + /* Verify that the anti-range doesn't make all arguments + invalid (treat the anti-range ~[0, 0] as invalid). */ + if (tree_int_cst_lt (maxobjsize, argrange[i][0]) + && tree_int_cst_le (argrange[i][1], integer_zero_node)) + { + warned + = warning_at (loc, OPT_Walloc_larger_than_, + (TYPE_UNSIGNED (type) + ? G_("argument %i range [%E, %E] exceeds " + "maximum object size %E") + : G_("argument %i range [%E, %E] is both " + "negative and exceeds maximum object " + "size %E")), + idx[i] + 1, argrange[i][0], + argrange[i][1], maxobjsize); + } + continue; + } + else + continue; + + /* Verify that the argument's range is not negative (including + upper bound of zero). */ + if (tree_int_cst_lt (argrange[i][0], integer_zero_node) + && tree_int_cst_le (argrange[i][1], integer_zero_node)) + { + warned = warning_at (loc, OPT_Walloc_larger_than_, + "argument %i range [%E, %E] is negative", + idx[i] + 1, argrange[i][0], argrange[i][1]); + } + else if (tree_int_cst_lt (maxobjsize, argrange[i][0])) + { + warned = warning_at (loc, OPT_Walloc_larger_than_, + "argument %i range [%E, %E] exceeds " + "maximum object size %E", + idx[i] + 1, argrange[i][0], argrange[i][1], + maxobjsize); + } + } + } + + if (!argrange[0]) + return; + + /* For a two-argument alloc_size, validate the product of the two + arguments if both of their values or ranges are known. */ + if (!warned && tree_fits_uhwi_p (argrange[0][0]) + && argrange[1][0] && tree_fits_uhwi_p (argrange[1][0])) + { + /* Check for overflow in the product of a function decorated with + attribute alloc_size (X, Y). */ + unsigned szprec = TYPE_PRECISION (size_type_node); + wide_int x = wi::to_wide (argrange[0][0], szprec); + wide_int y = wi::to_wide (argrange[1][0], szprec); + + bool vflow; + wide_int prod = wi::umul (x, y, &vflow); + + if (vflow) + warned = warning_at (loc, OPT_Walloc_larger_than_, + "product %<%E * %E%> of arguments %i and %i " + "exceeds %", + argrange[0][0], argrange[1][0], + idx[0] + 1, idx[1] + 1); + else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod)) + warned = warning_at (loc, OPT_Walloc_larger_than_, + "product %<%E * %E%> of arguments %i and %i " + "exceeds maximum object size %E", + argrange[0][0], argrange[1][0], + idx[0] + 1, idx[1] + 1, + maxobjsize); + + if (warned) + { + /* Print the full range of each of the two arguments to make + it clear when it is, in fact, in a range and not constant. */ + if (argrange[0][0] != argrange [0][1]) + inform (loc, "argument %i in the range [%E, %E]", + idx[0] + 1, argrange[0][0], argrange[0][1]); + if (argrange[1][0] != argrange [1][1]) + inform (loc, "argument %i in the range [%E, %E]", + idx[1] + 1, argrange[1][0], argrange[1][1]); + } + } + + if (warned) + { + if (EXPR_HAS_LOCATION (fn)) + inform (DECL_SOURCE_LOCATION (fn), + "in a call to allocation function %qD declared here", fn); + else + inform (loc, + "in a call to built-in allocation function %qD", fn); + } +} + /* Issue an error if CALL_EXPR was flagged as requiring tall-call optimization. */ @@ -1359,6 +1594,24 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, bitmap_obstack_release (NULL); + /* Extract attribute alloc_size and if set, store the indices of + the corresponding arguments in ALLOC_IDX, and then the actual + argument(s) at those indices in ALLOC_ARGS. */ + int alloc_idx[2] = { -1, -1 }; + if (tree alloc_size + = (fndecl ? lookup_attribute ("alloc_size", + TYPE_ATTRIBUTES (TREE_TYPE (fndecl))) + : NULL_TREE)) + { + tree args = TREE_VALUE (alloc_size); + alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; + if (TREE_CHAIN (args)) + alloc_idx[1] = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + } + + /* Array for up to the two attribute alloc_size arguments. */ + tree alloc_args[] = { NULL_TREE, NULL_TREE }; + /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ for (argpos = 0; argpos < num_actuals; i--, argpos++) { @@ -1595,6 +1848,20 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type), type, argpos < n_named_args); + + /* Store argument values for functions decorated with attribute + alloc_size. */ + if (argpos == alloc_idx[0]) + alloc_args[0] = args[i].tree_value; + else if (argpos == alloc_idx[1]) + alloc_args[1] = args[i].tree_value; + } + + if (alloc_args[0]) + { + /* Check the arguments of functions decorated with attribute + alloc_size. */ + maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx); } } diff --git a/gcc/calls.h b/gcc/calls.h index e144156..3b07263 100644 --- a/gcc/calls.h +++ b/gcc/calls.h @@ -37,7 +37,6 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode, tree, bool); extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode, tree, bool); - - +extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]); #endif // GCC_CALLS_H diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 76b8540..3f63283 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -256,6 +256,7 @@ Objective-C and Objective-C++ Dialects}. @gccoptlist{-fsyntax-only -fmax-errors=@var{n} -Wpedantic @gol -pedantic-errors @gol -w -Wextra -Wall -Waddress -Waggregate-return @gol +-Walloc-zero -Walloc-larger-than=@var{n} -Walloca -Walloca-larger-than=@var{n} @gol -Wno-aggressive-loop-optimizations -Warray-bounds -Warray-bounds=@var{n} @gol -Wno-attributes -Wbool-compare -Wbool-operation @gol @@ -4990,6 +4991,24 @@ annotations. Warn about overriding virtual functions that are not marked with the override keyword. +@item -Walloc-zero +@opindex Wno-alloc-zero +@opindex Walloc-zero +Warn about calls to allocation functions decorated with attribute +@code{alloc_size} that specify zero bytes, including those to the built-in +forms of the functions @code{aligned_alloc}, @code{alloca}, @code{calloc}, +@code{malloc}, and @code{realloc}. Because the behavior of these functions +when called with a zero size differs among implementations relying on it may +result in subtle portability bugs and should be avoided. This option is +enabled with @option{-Wextra} + +@item -Walloc-larger-than=@var{n} +Warn about calls to functions decorated with attribute @code{alloc_size} +that attempt to allocate objects larger than the specified number of bytes, +or where the result of the size computation in an integer type with infinite +precision would exceed @code{SIZE_MAX}. +@xref{Function Attributes}. + @item -Walloca @opindex Wno-alloca @opindex Walloca diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-3.c b/gcc/testsuite/gcc.dg/attr-alloc_size-3.c new file mode 100644 index 0000000..14d7efb --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-3.c @@ -0,0 +1,447 @@ +/* PR c/77531 - __attribute__((alloc_size(1,2))) could also warn on multiplication overflow + PR c/78284 - warn on malloc with very large arguments + Test exercising the ability to detect and diagnose calls to allocation + functions decorated with attribute alloc_size that either overflow or + exceed the maximum object size. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wall" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SCHAR_MIN (-SCHAR_MAX - 1) +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) + +#define SHRT_MAX __SHRT_MAX__ +#define SHRT_MIN (-SHRT_MAX - 1) +#define USHRT_MAX (SHRT_MAX * 2 + 1) + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (INT_MAX * 2U + 1) + +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-LONG_MAX - 1L) +#define ULONG_MAX (LONG_MAX * 2LU + 1) + +#define LLONG_MAX __LLONG_MAX__ +#define LLONG_MIN (-LLONG_MAX - 1LL) +#define ULLONG_MAX (ULLONG_MAX * 2LLU + 1) + +#define SIZE_MAX __SIZE_MAX__ + +typedef __SIZE_TYPE__ size_t; + +#define ALLOC_SIZE(...) __attribute__ ((alloc_size (__VA_ARGS__))) + +void* f_uchar_1 (unsigned char) ALLOC_SIZE (1); +void* f_uchar_2 (unsigned char, unsigned char) ALLOC_SIZE (1, 2); +void* f_schar_1 (signed char) ALLOC_SIZE (1); +void* f_schar_2 (signed char, signed char) ALLOC_SIZE (1, 2); + +void* f_ushrt_1 (unsigned short) ALLOC_SIZE (1); +void* f_ushrt_2 (unsigned short, unsigned short) ALLOC_SIZE (1, 2); +void* f_shrt_1 (signed short) ALLOC_SIZE (1); +void* f_shrt_2 (signed short, signed short) ALLOC_SIZE (1, 2); + +void* f_uint_1 (unsigned) ALLOC_SIZE (1); +void* f_uint_2 (unsigned, unsigned) ALLOC_SIZE (1, 2); +void* f_int_1 (int) ALLOC_SIZE (1); +void* f_int_2 (int, int) ALLOC_SIZE (1, 2); + +void* f_ulong_1 (unsigned long) ALLOC_SIZE (1); +void* f_ulong_2 (unsigned long, unsigned long) ALLOC_SIZE (1, 2); +void* f_long_1 (long) ALLOC_SIZE (1); +void* f_long_2 (long, long) ALLOC_SIZE (1, 2); + +void* f_ullong_1 (unsigned long long) ALLOC_SIZE (1); +void* f_ullong_2 (unsigned long long, unsigned long long) ALLOC_SIZE (1, 2); +void* f_llong_1 (long long) ALLOC_SIZE (1); +void* f_llong_2 (long long, long long) ALLOC_SIZE (1, 2); + +void* f_size_1 (size_t) ALLOC_SIZE (1); +void* f_size_2 (size_t, size_t) ALLOC_SIZE (1, 2); + +unsigned long long +unsigned_range (unsigned long long min, unsigned long long max) +{ + extern unsigned long long random_unsigned_value (void); + unsigned long long val = random_unsigned_value (); + if (val < min || max < val) val = min; + return val; +} + +long long +signed_range (long long min, long long max) +{ + extern long long random_signed_value (void); + long long val = random_signed_value (); + if (val < min || max < val) val = min; + return val; +} + +unsigned long long +unsigned_anti_range (unsigned long long min, unsigned long long max) +{ + extern unsigned long long random_unsigned_value (void); + unsigned long long val = random_unsigned_value (); + if (min <= val && val <= max) + val = min - 1; + return val; +} + +long long +signed_anti_range (long long min, long long max) +{ + extern long long random_signed_value (void); + long long val = random_signed_value (); + if (min <= val && val <= max) + val = min - 1; + return val; +} + +#define UR(min, max) unsigned_range (min, max) +#define SR(min, max) signed_range (min, max) + +#define UAR(min, max) unsigned_anti_range (min, max) +#define SAR(min, max) signed_anti_range (min, max) + + +void sink (void*); + +void +test_uchar_cst (void) +{ + const unsigned char max = UCHAR_MAX; + + sink (f_uchar_1 (0)); + sink (f_uchar_1 (1)); + sink (f_uchar_1 (max)); + + sink (f_uchar_2 (0, 0)); + sink (f_uchar_2 (0, 1)); + sink (f_uchar_2 (1, 0)); + sink (f_uchar_2 (1, 1)); + sink (f_uchar_2 (0, max)); + sink (f_uchar_2 (max, 0)); + sink (f_uchar_2 (max, max)); +} + +void +test_uchar_range (unsigned char n) +{ + const unsigned char max = UCHAR_MAX; + + sink (f_uchar_1 (n)); + + sink (f_uchar_1 (UR (0, 1))); + sink (f_uchar_1 (UR (1, max))); + sink (f_uchar_1 (UR (0, max - 1))); + + sink (f_uchar_1 (UAR (1, 1))); + sink (f_uchar_1 (UAR (1, max - 1))); + sink (f_uchar_1 (UAR (max - 2, max - 1))); + + sink (f_uchar_2 (0, n)); + sink (f_uchar_2 (n, 0)); + sink (f_uchar_2 (1, n)); + sink (f_uchar_2 (n, 1)); + sink (f_uchar_2 (max, n)); + sink (f_uchar_2 (n, max)); + sink (f_uchar_2 (n, n)); + + sink (f_uchar_2 (UR (0, 1), UR (0, 1))); + sink (f_uchar_2 (UR (1, 2), UR (1, 2))); + sink (f_uchar_2 (UR (1, max), UR (0, 1))); + sink (f_uchar_2 (UR (0, 1), UR (1, max))); +} + +void +test_schar_cst (void) +{ + const signed char min = SCHAR_MIN; + const signed char max = SCHAR_MAX; + + sink (f_schar_1 (min)); /* { dg-warning "argument 1 value .-\[0-9\]+. is negative" } */ + sink (f_schar_1 (-1)); /* { dg-warning "argument 1 value .-1. is negative" } */ + sink (f_schar_1 (0)); + sink (f_schar_1 (1)); + sink (f_schar_1 (max)); + + sink (f_schar_2 (0, min)); /* { dg-warning "argument 2 value .-\[0-9\]+. is negative" } */ + sink (f_schar_2 (min, 0)); /* { dg-warning "argument 1 value .-\[0-9\]+. is negative" } */ + sink (f_schar_2 (0, -1)); /* { dg-warning "argument 2 value .-1. is negative" } */ + sink (f_schar_2 (-1, 0)); /* { dg-warning "argument 1 value .-1. is negative" } */ + +} + +void +test_schar_range (signed char n) +{ + const signed char min = SCHAR_MIN; + const signed char max = SCHAR_MAX; + + sink (f_schar_1 (n)); + + sink (f_schar_1 (SR (min, min + 1))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_schar_1 (SR (min, 0))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_schar_1 (SR (-1, 0))); /* { dg-warning "argument 1 range \\\[-1, 0\\\] is negative" } */ + sink (f_schar_1 (SR (-1, 1))); + sink (f_schar_1 (SR (0, 1))); + sink (f_schar_1 (SR (0, max - 1))); + sink (f_schar_1 (SR (1, max))); + sink (f_schar_1 (SR (max - 1, max))); + + sink (f_schar_2 (n, n)); + + sink (f_schar_2 (SR (min, min + 1), n)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_schar_2 (n, SR (min, min + 1))); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_schar_2 (SR (min, min + 1), 0)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_schar_2 (0, SR (min, min + 1))); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_schar_2 (SR (min, min + 1), min)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + /* { dg-warning "argument 2 value .-\[0-9\]+. is negative" "argument 2" { target *-*-* } .-1 } */ + sink (f_schar_2 (min, SR (min, min + 1))); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + /* { dg-warning "argument 1 value .-\[0-9\]+. is negative" "argument 1" { target *-*-* } .-1 } */ + + sink (f_schar_2 (SR (-1, 0), 0)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_schar_2 (0, SR (-1, 0))); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_schar_2 (SR (-1, 0), 1)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_schar_2 (1, SR (-1, 0))); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_schar_2 (SR (-1, 0), n)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_schar_2 (n, SR (-1, 0))); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + + sink (f_schar_2 (max, SR (1, max))); + sink (f_schar_2 (SR (1, max), max)); +} + +void +test_ushrt_cst (void) +{ + const unsigned short max = USHRT_MAX; + + sink (f_ushrt_1 (0)); + sink (f_ushrt_1 (1)); + sink (f_ushrt_1 (max)); + + sink (f_ushrt_2 (0, 0)); + sink (f_ushrt_2 (0, 1)); + sink (f_ushrt_2 (1, 0)); + sink (f_ushrt_2 (1, 1)); + sink (f_ushrt_2 (0, max)); + sink (f_ushrt_2 (max, 0)); + +#if USHRT_MAX < SIZE_MAX + sink (f_ushrt_2 (max, max)); +#endif +} + +void +test_ushrt_range (unsigned short n) +{ + const unsigned short max = USHRT_MAX; + + sink (f_ushrt_1 (n)); + sink (f_ushrt_1 (UR (0, 1))); + sink (f_ushrt_1 (UR (1, max - 1))); + sink (f_ushrt_1 (UR (1, max))); + sink (f_ushrt_1 (UR (0, max - 1))); +} + +void +test_shrt_cst (void) +{ + const short min = SHRT_MIN; + const short max = SHRT_MAX; + + sink (f_shrt_1 (min)); /* { dg-warning "argument 1 value .-\[0-9\]+. is negative" } */ + sink (f_shrt_1 (-1)); /* { dg-warning "argument 1 value .-1. is negative" } */ + sink (f_shrt_1 (0)); + sink (f_shrt_1 (1)); + sink (f_shrt_1 (max)); +} + +void +test_shrt_range (short n) +{ + const short min = SHRT_MIN; + const short max = SHRT_MAX; + + sink (f_shrt_1 (n)); + + sink (f_shrt_1 (SR (min, min + 1))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_shrt_1 (SR (min, 0))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_shrt_1 (SR (-1, 0))); /* { dg-warning "argument 1 range \\\[-1, 0\\\] is negative" } */ + sink (f_shrt_1 (SR (-1, 1))); + sink (f_shrt_1 (SR (0, 1))); + sink (f_shrt_1 (SR (0, max - 1))); + sink (f_shrt_1 (SR (1, max))); + sink (f_shrt_1 (SR (max - 1, max))); +} + +void +test_uint_cst (void) +{ + const unsigned max = UINT_MAX; + + sink (f_uint_1 (0)); + sink (f_uint_1 (1)); + sink (f_uint_1 (max - 1)); + sink (f_uint_1 (max)); +} + +void +test_uint_range (unsigned n) +{ + const unsigned max = UINT_MAX; + + sink (f_uint_1 (n)); + sink (f_uint_1 (UR (0, 1))); + sink (f_uint_1 (UR (0, max - 1))); + sink (f_uint_1 (UR (1, max - 1))); + sink (f_uint_1 (UR (1, max))); +} + +void +test_int_cst (void) +{ + const int min = INT_MIN; + const int max = INT_MAX; + + sink (f_int_1 (min)); /* { dg-warning "argument 1 value .-\[0-9\]+. is negative" } */ + sink (f_int_1 (-1)); /* { dg-warning "argument 1 value .-1. is negative" } */ + sink (f_int_1 (0)); + sink (f_int_1 (1)); + sink (f_int_1 (max)); +} + +void +test_int_range (int n) +{ + const int min = INT_MIN; + const int max = INT_MAX; + + sink (f_int_1 (n)); + + sink (f_int_1 (SR (min, min + 1))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -\[0-9\]+\\\] is negative" } */ + sink (f_int_1 (SR (min, 0))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ + sink (f_int_1 (SR (-1, 0))); /* { dg-warning "argument 1 range \\\[-1, 0\\\] is negative" } */ + sink (f_int_1 (SR (-1, 1))); + sink (f_int_1 (SR (0, 1))); + sink (f_int_1 (SR (0, max - 1))); + sink (f_int_1 (SR (1, max))); + sink (f_int_1 (SR (max - 1, max))); +} + +void +test_ulong_cst (void) +{ + const unsigned long max = ULONG_MAX; + + sink (f_ulong_1 (0)); + sink (f_ulong_1 (1)); +#if ULONG_MAX < SIZE_MAX + sink (f_ulong_1 (max - 1)); + sink (f_ulong_1 (max)); +#else + (void)&max; +#endif +} + +void +test_ulong_range (unsigned long n) +{ + const unsigned long max = ULONG_MAX; + + sink (f_ulong_1 (n)); + sink (f_ulong_1 (UR (0, 1))); + sink (f_ulong_1 (UR (0, max - 1))); + sink (f_ulong_1 (UR (1, max - 1))); + sink (f_ulong_1 (UR (1, max))); +} + +void +test_long_cst (void) +{ + const long min = LONG_MIN; + const long max = LONG_MAX; + + sink (f_long_1 (min)); /* { dg-warning "argument 1 value .-\[0-9\]+l*. is negative" } */ + sink (f_long_1 (-1)); /* { dg-warning "argument 1 value .-1l*. is negative" } */ + sink (f_long_1 (0)); + sink (f_long_1 (1)); + sink (f_long_1 (max)); +} + +void +test_long_range (long n) +{ + const long min = LONG_MIN; + const long max = LONG_MAX; + + sink (f_long_1 (n)); + + sink (f_long_1 (SR (min, min + 1))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+l*, -\[0-9\]+l*\\\] is negative" } */ + sink (f_long_1 (SR (min, 0))); /* { dg-warning "argument 1 range \\\[-\[0-9\]+l*, 0l*\\\] is negative" } */ + sink (f_long_1 (SR (-1, 0))); /* { dg-warning "argument 1 range \\\[-1l*, 0l*\\\] is negative" } */ + sink (f_long_1 (SR (-1, 1))); + sink (f_long_1 (SR (0, 1))); + sink (f_long_1 (SR (0, max - 1))); + sink (f_long_1 (SR (1, max))); + sink (f_long_1 (SR (max - 1, max))); +} + +void +test_size_cst (void) +{ + const size_t max = __SIZE_MAX__; + + sink (f_size_1 (0)); + sink (f_size_1 (1)); + sink (f_size_1 (max - 1)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + sink (f_size_1 (max)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + + sink (f_size_2 (0, max - 1)); /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (max - 1, 0)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (1, max - 1)); /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (max - 1, 1)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (max - 1, max - 1)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" "argument 2" { target *-*-* } .-1 } */ + + sink (f_size_2 (0, max)); /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (max, 0)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size \[0-9\]+" } */ + + sink (f_size_2 (max / 2, 2)); /* { dg-warning "product .\[0-9\]+\[lu\]* \\* \[0-9\]+\[lu\]*. of arguments 1 and 2 exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (max / 2, 3)); /* { dg-warning "product .\[0-9\]+\[lu\]* \\* \[0-9\]+\[lu\]*. of arguments 1 and 2 exceeds .SIZE_MAX." } */ +} + +void +test_size_range (size_t n) +{ + const size_t max = __SIZE_MAX__; + + sink (f_size_1 (n)); + + sink (f_size_1 (UR (0, 1))); + sink (f_size_1 (UR (0, max - 1))); + sink (f_size_1 (UR (1, max - 1))); + sink (f_size_1 (UR (1, max))); + + sink (f_size_1 (UAR (1, 1))); + /* Since the only valid argument in the anti-range below is zero + a warning is expected even though -Walloc-zero is not specified. */ + sink (f_size_1 (UAR (1, max / 2))); /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */ + /* The only valid argument in this range is 1. */ + sink (f_size_1 (UAR (2, max / 2))); + + sink (f_size_2 (n, n)); + sink (f_size_2 (n, max / 2)); + sink (f_size_2 (max / 2, n)); + + sink (f_size_2 (UR (0, 1), max)); /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size " } */ + sink (f_size_2 (UR (0, 1), max / 2)); + sink (f_size_2 (UR (0, max / 2), max / 2)); + + sink (f_size_2 (UR (max / 2 + 1, max / 2 + 2), n)); /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */ + sink (f_size_2 (n, UR (max / 2 + 1, max / 2 + 2))); /* { dg-warning "argument 2 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */ + sink (f_size_2 (UR (max / 2 + 1, max), UR (max / 2 + 1, max))); /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */ +/* { dg-warning "argument 2 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " "argument 2" { target *-*-* } .-1 } */ + +} diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-4.c b/gcc/testsuite/gcc.dg/attr-alloc_size-4.c new file mode 100644 index 0000000..24a2696 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-4.c @@ -0,0 +1,190 @@ +/* PR c/77531 - __attribute__((alloc_size(1,2))) could also warn on multiplication overflow + PR c/78284 - warn on malloc with very large arguments + Test exercising the ability to detect and diagnose calls to allocation + functions decorated with attribute alloc_size that either overflow or + exceed the maximum object size. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wall -Walloc-larger-than=1234" } */ + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (INT_MAX * 2U + 1) + +#define SIZE_MAX __SIZE_MAX__ + +typedef __SIZE_TYPE__ size_t; + +#define ALLOC_SIZE(...) __attribute__ ((alloc_size (__VA_ARGS__))) + +void* f_uint_1 (unsigned) ALLOC_SIZE (1); +void* f_uint_2 (unsigned, unsigned) ALLOC_SIZE (1, 2); +void* f_int_1 (int) ALLOC_SIZE (1); +void* f_int_2 (int, int) ALLOC_SIZE (1, 2); + +void* f_size_1 (size_t) ALLOC_SIZE (1); +void* f_size_2 (size_t, size_t) ALLOC_SIZE (1, 2); + +size_t +unsigned_range (size_t min, size_t max) +{ + extern size_t random_unsigned_value (void); + size_t val = random_unsigned_value (); + if (val < min || max < val) val = min; + return val; +} + +int +signed_range (int min, int max) +{ + extern int random_signed_value (void); + int val = random_signed_value (); + if (val < min || max < val) val = min; + return val; +} + +size_t +unsigned_anti_range (size_t min, size_t max) +{ + extern size_t random_unsigned_value (void); + size_t val = random_unsigned_value (); + if (min <= val && val <= max) + val = min - 1; + return val; +} + +int +signed_anti_range (int min, int max) +{ + extern int random_signed_value (void); + int val = random_signed_value (); + if (min <= val && val <= max) + val = min - 1; + return val; +} + +#define UR(min, max) unsigned_range (min, max) +#define SR(min, max) signed_range (min, max) + +#define UAR(min, max) unsigned_anti_range (min, max) +#define SAR(min, max) signed_anti_range (min, max) + + +void sink (void*); + +void +test_uint_cst (void) +{ + const unsigned max = UINT_MAX; + + sink (f_uint_1 (0)); + sink (f_uint_1 (1)); + sink (f_uint_1 (1233)); + sink (f_uint_1 (1234)); + sink (f_uint_1 (1235)); /* { dg-warning "argument 1 value .1235u?. exceeds maximum object size 1234" } */ + sink (f_uint_1 (max - 1)); /* { dg-warning "argument 1 value .\[0-9\]+u?. exceeds maximum object size 1234" } */ + sink (f_uint_1 (max)); /* { dg-warning "argument 1 value .\[0-9\]+u?. exceeds maximum object size 1234" } */ +} + +void +test_uint_range (unsigned n) +{ + const unsigned max = UINT_MAX; + + sink (f_uint_1 (n)); + sink (f_uint_1 (UR (0, 1))); + sink (f_uint_1 (UR (0, 1233))); + sink (f_uint_1 (UR (0, 1234))); + sink (f_uint_1 (UR (0, 1235))); + sink (f_uint_1 (UR (1, 1235))); + sink (f_uint_1 (UR (1234, 1235))); + sink (f_uint_1 (UR (1235, 1236))); /* { dg-warning "argument 1 range \\\[\[0-9\]+u?, \[0-9\]+u?\\\] exceeds maximum object size 1234" } */ + sink (f_uint_1 (UR (1, max - 1))); + sink (f_uint_1 (UR (1, max))); +} + +void +test_int_cst (void) +{ + const int min = INT_MIN; + const int max = INT_MAX; + + sink (f_int_1 (min)); /* { dg-warning "argument 1 value .-\[0-9\]+. is negative" } */ + sink (f_int_1 (-1)); /* { dg-warning "argument 1 value .-1. is negative" } */ + sink (f_int_1 (0)); + sink (f_int_1 (1)); + sink (f_int_1 (1233)); + sink (f_int_1 (1234)); + sink (f_int_1 (max)); /* { dg-warning "argument 1 value .\[0-9\]+u?. exceeds maximum object size 1234" } */ +} + +void +test_int_range (int n) +{ + const int min = INT_MIN; + const int max = INT_MAX; + + sink (f_int_1 (n)); + + sink (f_int_1 (SR (min, 1234))); + sink (f_int_1 (SR (-2, -1))); /* { dg-warning "argument 1 range \\\[-2, -1\\\] is negative" } */ + sink (f_int_1 (SR (1235, 2345))); /* { dg-warning "argument 1 range \\\[1235, 2345\\\] exceeds maximum object size 1234" } */ + sink (f_int_1 (SR (max - 1, max))); /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size 1234" } */ + + sink (f_int_1 (SAR (-1, 1))); + sink (f_int_1 (SAR (-2, 12))); + sink (f_int_1 (SAR (-3, 123))); + sink (f_int_1 (SAR (-4, 1234))); /* { dg-warning "argument 1 range \\\[1235, -5\\\] is both negative and exceeds maximum object size 1234" } */ + sink (f_int_1 (SAR (min + 1, 1233))); + sink (f_int_1 (SAR (min + 2, 1235))); /* { dg-warning "argument 1 range \\\[1236, -\[0-9\]+\\\] is both negative and exceeds maximum object size 1234" } */ +} + +void +test_size_cst (void) +{ + const size_t max = __SIZE_MAX__; + + sink (f_size_1 (0)); + sink (f_size_1 (1)); + + sink (f_size_2 ( 0, 1234)); + sink (f_size_2 ( 1, 1234)); + sink (f_size_2 ( 2, 1234)); /* { dg-warning "product .2\[lu\]* \\* 1234\[lu\]*. of arguments 1 and 2 exceeds maximum object size \[0-9\]+" } */ + sink (f_size_2 (1234, 1234)); /* { dg-warning "product .1234\[lu\]* \\* 1234\[lu\]*. of arguments 1 and 2 exceeds maximum object size 1234" } */ + sink (f_size_2 (1235, 1234)); /* { dg-warning "argument 1 value .1235\[lu\]*. exceeds maximum object size 1234" } */ + sink (f_size_2 (1234, 1235)); /* { dg-warning "argument 2 value .1235\[lu\]*. exceeds maximum object size 1234" } */ + sink (f_size_2 (1234, max)); /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size 1234" } */ + sink (f_size_2 (max, 1234)); /* { dg-warning "argument 1 value .\[0-9\]+\[lu\]*. exceeds maximum object size 1234" } */ +} + +void +test_size_range (size_t n) +{ + const size_t max = __SIZE_MAX__; + + sink (f_size_1 (n)); + + sink (f_size_1 (UR (0, 1))); + sink (f_size_1 (UR (0, max - 1))); + sink (f_size_1 (UR (1, max - 1))); + sink (f_size_1 (UR (1, max))); + + sink (f_size_1 (UAR (1, 1))); + /* Since the only valid argument in the anti-range below is zero + a warning is expected even though -Walloc-zero is not specified. */ + sink (f_size_1 (UAR (1, 1234))); /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */ + /* The only valid argument in this range is 1. */ + sink (f_size_1 (UAR (2, max / 2))); + + sink (f_size_2 (n, n)); + sink (f_size_2 (n, 1234)); + sink (f_size_2 (1234, n)); + + sink (f_size_2 (UR (0, 1), 1234)); + sink (f_size_2 (UR (0, 1), 1235)); /* { dg-warning "argument 2 value .1235\[lu\]*. exceeds maximum object size 1234" } */ + + sink (f_size_2 (UR (1235, 1236), n)); /* { dg-warning "argument 1 range \\\[1235\[lu\]*, 1236\[lu\]*\\\] exceeds maximum object size 1234" } */ + + sink (f_size_2 (UR (1235, 1236), UR (max / 2, max))); /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */ +/* { dg-warning "argument 2 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " "argument 2" { target *-*-* } .-1 } */ + +} diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-5.c b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c new file mode 100644 index 0000000..4e37ba5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c @@ -0,0 +1,199 @@ +/* PR c/78284 - warn on malloc with very large arguments + Test exercising the ability to detect and diagnose calls to allocation + functions decorated with attribute alloc_size that attempt to allocate + zero bytes. For standard allocation functions the return value is + implementation-defined and so relying on it may be a source of bugs. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wall -Walloc-zero" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SCHAR_MIN (-SCHAR_MAX - 1) +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) + +#define SHRT_MAX __SHRT_MAX__ +#define SHRT_MIN (-SHRT_MAX - 1) +#define USHRT_MAX (SHRT_MAX * 2 + 1) + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (INT_MAX * 2U + 1) + +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-LONG_MAX - 1L) +#define ULONG_MAX (LONG_MAX * 2LU + 1) + +#define LLONG_MAX __LLONG_MAX__ +#define LLONG_MIN (-LLONG_MAX - 1LL) +#define ULLONG_MAX (ULLONG_MAX * 2LLU + 1) + +#define SIZE_MAX __SIZE_MAX__ + +typedef __SIZE_TYPE__ size_t; + + +#define ALLOC_SIZE(...) __attribute__ ((alloc_size (__VA_ARGS__))) + +void* f_uchar_1 (unsigned char) ALLOC_SIZE (1); +void* f_uchar_2 (unsigned char, unsigned char) ALLOC_SIZE (1, 2); +void* f_schar_1 (signed char) ALLOC_SIZE (1); +void* f_schar_2 (signed char, signed char) ALLOC_SIZE (1, 2); + +void* f_ushrt_1 (unsigned short) ALLOC_SIZE (1); +void* f_ushrt_2 (unsigned short, unsigned short) ALLOC_SIZE (1, 2); +void* f_shrt_1 (signed short) ALLOC_SIZE (1); +void* f_shrt_2 (signed short, signed short) ALLOC_SIZE (1, 2); + +void* f_uint_1 (unsigned) ALLOC_SIZE (1); +void* f_uint_2 (unsigned, unsigned) ALLOC_SIZE (1, 2); +void* f_int_1 (int) ALLOC_SIZE (1); +void* f_int_2 (int, int) ALLOC_SIZE (1, 2); + +void* f_ulong_1 (unsigned long) ALLOC_SIZE (1); +void* f_ulong_2 (unsigned long, unsigned long) ALLOC_SIZE (1, 2); +void* f_long_1 (long) ALLOC_SIZE (1); +void* f_long_2 (long, long) ALLOC_SIZE (1, 2); + +void* f_ullong_1 (unsigned long long) ALLOC_SIZE (1); +void* f_ullong_2 (unsigned long long, unsigned long long) ALLOC_SIZE (1, 2); +void* f_llong_1 (long long) ALLOC_SIZE (1); +void* f_llong_2 (long long, long long) ALLOC_SIZE (1, 2); + +void* f_size_1 (size_t) ALLOC_SIZE (1); +void* f_size_2 (size_t, size_t) ALLOC_SIZE (1, 2); + +void sink (void*); + +void +test_uchar (unsigned char n) +{ + sink (f_uchar_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_uchar_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_uchar_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_uchar_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_uchar_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_uchar_1 (n)); + n = 0; + sink (f_uchar_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_uchar_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_schar (signed char n) +{ + sink (f_schar_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_schar_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_schar_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_schar_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_schar_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_schar_1 (n)); + n = 0; + sink (f_schar_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_schar_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_ushrt (unsigned short n) +{ + sink (f_ushrt_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_ushrt_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_ushrt_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_ushrt_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_ushrt_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_ushrt_1 (n)); + n = 0; + sink (f_ushrt_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_ushrt_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_shrt (short n) +{ + sink (f_shrt_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_shrt_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_shrt_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_shrt_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_shrt_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_shrt_1 (n)); + n = 0; + sink (f_shrt_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_shrt_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_uint (unsigned n) +{ + sink (f_uint_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_uint_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_uint_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_uint_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_uint_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_uint_1 (n)); + n = 0; + sink (f_uint_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_uint_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_int (int n) +{ + sink (f_int_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_int_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_int_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_int_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_int_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_int_1 (n)); + n = 0; + sink (f_int_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_int_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_ulong (unsigned long n) +{ + sink (f_ulong_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_ulong_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_ulong_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_ulong_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_ulong_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_ulong_1 (n)); + n = 0; + sink (f_ulong_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_ulong_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_long (long n) +{ + sink (f_long_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_long_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_long_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_long_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_long_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_long_1 (n)); + n = 0; + sink (f_long_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_long_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} + +void +test_size (size_t n) +{ + sink (f_size_1 (0)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_size_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_size_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_size_2 (n, 0)); /* { dg-warning "argument 2 value is zero" } */ + sink (f_size_2 (0, n)); /* { dg-warning "argument 1 value is zero" } */ + + sink (f_size_1 (n)); + n = 0; + sink (f_size_1 (n)); /* { dg-warning "argument 1 value is zero" } */ + sink (f_size_2 (1, n)); /* { dg-warning "argument 2 value is zero" } */ +} diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-6.c b/gcc/testsuite/gcc.dg/attr-alloc_size-6.c new file mode 100644 index 0000000..de5c3f3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-6.c @@ -0,0 +1,45 @@ +/* PR c/78284 - warn on malloc with very large arguments + Test exercising the ability of the built-in allocation functions + to detect and diagnose, without optimization, calls that attemnpt + to allocate objects in excess of the number of bytes specified by + -Walloc-larger-than=maximum. */ +/* { dg-do compile } */ +/* { dg-options "-O0 -Wall -Walloc-larger-than=12345" } */ + +#define MAXOBJSZ 12345 + +typedef __SIZE_TYPE__ size_t; + +void sink (void*); + + +void test_lit (void *p) +{ + sink (__builtin_aligned_alloc (MAXOBJSZ + 1, 1)); /* { dg-warning "argument 1 value .12346\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_alloca (MAXOBJSZ + 2)); /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_calloc (MAXOBJSZ / 2, 3)); /* { dg-warning "product .6172\[lu\]* \\* 3\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + sink (__builtin_calloc (4, MAXOBJSZ / 3)); /* { dg-warning "product .4\[lu\]* \\* 4115\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + + sink (__builtin_malloc (MAXOBJSZ + 3)); /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_realloc (p, MAXOBJSZ + 4)); /* { dg-warning "argument 2 value .12349\[lu\]*. exceeds maximum object size 12345" } */ +} + + +enum { max = MAXOBJSZ }; + +void test_cst (void *p) +{ + sink (__builtin_aligned_alloc (max + 1, 1)); /* { dg-warning "argument 1 value .12346\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_alloca (max + 2)); /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_calloc (max / 2, 3)); /* { dg-warning "product .6172\[lu\]* \\* 3\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + sink (__builtin_calloc (4, max / 3)); /* { dg-warning "product .4\[lu\]* \\* 4115\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + + sink (__builtin_malloc (max + 3)); /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_realloc (p, max + 4)); /* { dg-warning "argument 2 value .12349\[lu\]*. exceeds maximum object size 12345" } */ +} diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-7.c b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c new file mode 100644 index 0000000..4967630 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c @@ -0,0 +1,58 @@ +/* PR c/78284 - warn on malloc with very large arguments + Test exercising the ability of the built-in allocation functions to + detect and diagnose calls that attemnpt to allocate objects in excess + of the maximum specified by -Walloc-larger-than=maximum. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wall -Walloc-larger-than=12345" } */ + +#define SIZE_MAX __SIZE_MAX__ +#define MAXOBJSZ 12345 + +typedef __SIZE_TYPE__ size_t; + +void sink (void*); + +size_t maxobjsize (void) +{ + return MAXOBJSZ; +} + + +void test_var (void *p) +{ + size_t max = maxobjsize (); + + sink (__builtin_aligned_alloc (max + 1, 1)); /* { dg-warning "argument 1 value .12346\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_alloca (max + 2)); /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_calloc (max / 2, 3)); /* { dg-warning "product .6172\[lu\]* \\* 3\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + sink (__builtin_calloc (4, max / 3)); /* { dg-warning "product .4\[lu\]* \\* 4115\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + + sink (__builtin_malloc (max + 3)); /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */ + + sink (__builtin_realloc (p, max + 4)); /* { dg-warning "argument 2 value .12349\[lu\]*. exceeds maximum object size 12345" } */ +} + + +void test_range (void *p, size_t range) +{ + /* Make sure the variable is at least as large as the maximum object + size but also make sure that it's guaranteed not to be too big to + increment (and wrap around). */ + size_t max = maxobjsize (); + + if (range < max || 2 * max <= range) + range = maxobjsize (); + + sink (__builtin_aligned_alloc (range + 1, 1)); /* { dg-warning "argument 1 range \\\[12346\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size 12345" } */ + + sink (__builtin_alloca (range + 2)); /* { dg-warning "argument 1 range \\\[12347\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size 12345" } */ + + sink (__builtin_calloc (range / 2, 3)); /* { dg-warning "product .6172\[lu\]* \\* 3\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + sink (__builtin_calloc (4, range / 3)); /* { dg-warning "product .4\[lu\]* \\* 4115\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */ + + sink (__builtin_malloc (range + 3)); /* { dg-warning "argument 1 range \\\[12348\[lu\]*, 24692\[lu\]*\\\] exceeds maximum object size 12345" } */ + + sink (__builtin_realloc (p, range + 4)); /* { dg-warning "argument 2 range \\\[12349\[lu\]*, 24693\[lu\]*\\\] exceeds maximum object size 12345" } */ +} diff --git a/include/libiberty.h b/include/libiberty.h index 605ff56..eab0fae 100644 --- a/include/libiberty.h +++ b/include/libiberty.h @@ -353,7 +353,7 @@ extern unsigned int xcrc32 (const unsigned char *, int, unsigned int); /* Array allocators. */ -#define XALLOCAVEC(T, N) ((T *) alloca (sizeof (T) * (N))) +#define XALLOCAVEC(T, N) ((T *) alloca ((sizeof (T) * (N)) | 1)) #define XNEWVEC(T, N) ((T *) xmalloc (sizeof (T) * (N))) #define XCNEWVEC(T, N) ((T *) xcalloc ((N), sizeof (T))) #define XDUPVEC(T, P, N) ((T *) xmemdup ((P), sizeof (T) * (N), sizeof (T) * (N)))