PR c/77531 - __attribute__((alloc_size(1,2))) could also warn on multiplication overflow
authorMartin Sebor <msebor@redhat.com>
Thu, 8 Dec 2016 23:50:40 +0000 (23:50 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Thu, 8 Dec 2016 23:50:40 +0000 (16:50 -0700)
PR c/77531 - __attribute__((alloc_size(1,2))) could also warn on multiplication overflow
PR c/78284 - warn on malloc with very large arguments

gcc/c-family/ChangeLog:

PR c/78284
* c.opt (-Walloc-zero, -Walloc-size-larger-than): New options.

gcc/ChangeLog:

PR c/78284
* builtin-attrs.def (ATTR_ALLOC_SIZE, ATTR_RETURNS_NONNULL): New
identifier tree nodes.
(ATTR_ALLOCA_SIZE_1_NOTHROW_LEAF_LIST): New attribute list.
(ATTR_MALLOC_SIZE_1_NOTHROW_LIST): Same.
(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 (aligned_alloc, calloc, malloc, realloc):
Add attribute alloc_size.
(alloca): Add attribute alloc_size and returns_nonnull.
* calls.h (maybe_warn_alloc_args_overflow): Declare.
* calls.c (alloc_max_size, operand_signed_p): New functions.
(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-size-larger-than.

gcc/testsuite/ChangeLog:

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.
* gcc.dg/attr-alloc_size-8.c: New test.
* gcc.dg/attr-alloc_size-9.c: New test.
* gcc/testsuite/gcc.dg/errno-1.c: Adjust.

From-SVN: r243470

16 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/c-family/ChangeLog
gcc/c-family/c.opt
gcc/calls.c
gcc/calls.h
gcc/doc/invoke.texi
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/attr-alloc_size-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/errno-1.c

index c9371f630311cfad56b3df969a5419306e854fa0..f8455c5977979155bcdd8f5605c31b2504235561 100644 (file)
@@ -1,3 +1,26 @@
+2016-12-08  Martin Sebor  <msebor@redhat.com>
+
+       PR c/78284
+       * builtin-attrs.def (ATTR_ALLOC_SIZE, ATTR_RETURNS_NONNULL): New
+       identifier tree nodes.
+       (ATTR_ALLOCA_SIZE_1_NOTHROW_LEAF_LIST): New attribute list.
+       (ATTR_MALLOC_SIZE_1_NOTHROW_LIST): Same.
+       (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 (aligned_alloc, calloc, malloc, realloc):
+       Add attribute alloc_size.
+       (alloca): Add attribute alloc_size and returns_nonnull.
+       * calls.h (maybe_warn_alloc_args_overflow): Declare.
+       * calls.c (alloc_max_size, operand_signed_p): New functions.
+       (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-size-larger-than.
+
 2016-12-08  Vladimir Makarov  <vmakarov@redhat.com>
 
        PR rtl-optimization/78671
index b58056cf334467cac0512f62d20bf4ffb0671f30..20515e74235f3fc9f78ec78fbf20265ce649589f 100644 (file)
@@ -4797,12 +4797,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));
@@ -4810,6 +4810,18 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate)
   if (!valid_arglist)
     return NULL_RTX;
 
+  if ((alloca_with_align && !warn_vla_limit)
+      || (!alloca_with_align && !warn_alloca_limit))
+    {
+      /* -Walloca-larger-than and -Wvla-larger-than settings override
+        the more general -Walloc-size-larger-than so unless either of
+        the former options is specified check the alloca arguments for
+        overflow.  */
+      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));
 
index 850b6f8b52c83df2b449a123ea837691263be13f..d6e1b8b15b04735e013bdc3e6ce9ef39a5062721 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-08  Martin Sebor  <msebor@redhat.com>
+
+       PR c/78284
+       * c.opt (-Walloc-zero, -Walloc-size-larger-than): New options.
+
 2016-12-08  Martin Sebor  <msebor@redhat.com>
 
        PR c/78165
index 288e4ce12fbf760e92e6ca4e60270c7c2279ba91..33c5def759296d22438560c65c7fc215a5e6ea71 100644 (file)
@@ -303,6 +303,15 @@ Walloca
 C ObjC C++ ObjC++ Var(warn_alloca) Warning
 Warn on any use of alloca.
 
+Walloc-size-larger-than=
+C ObjC C++ ObjC++ Var(warn_alloc_size_limit) Warning Joined
+-Walloc-size-larger-than=<bytes> 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
+-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=<number> Warn on unbounded uses of
index 530e7bf219dfff6813ca7119ab3adcb21f0e894c..bc3cbd599fc321a5422a2a7b214952eb14aded71 100644 (file)
@@ -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,311 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals)
       }
 }
 
+/* The limit set by -Walloc-larger-than=.  */
+static GTY(()) tree alloc_object_size_limit;
+
+/* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-size-larger-than=
+   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_size_limit ? strtoull (warn_alloc_size_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;
+}
+
+/* Return true if the type of OP is signed, looking through any casts
+   to an unsigned type.  */
+
+static bool
+operand_signed_p (tree op)
+{
+  if (TREE_CODE (op) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (op);
+      if (is_gimple_assign (def))
+       {
+         /* In an assignment involving a cast, ignore the type
+            of the cast and consider the type of its  operand.  */
+         tree_code code = gimple_assign_rhs_code (def);
+         if (code == NOP_EXPR)
+           op = gimple_assign_rhs1 (def);
+       }
+      else if (gimple_code (def) == GIMPLE_PHI)
+       {
+         /* In a phi, a constant argument may be unsigned even
+            if in the source it's signed and negative.  Ignore
+            those and consider the result of a phi signed if
+            all its non-constant operands are.  */
+         unsigned nargs = gimple_phi_num_args (def);
+         for (unsigned i = 0; i != nargs; ++i)
+           {
+             tree op = gimple_phi_arg_def (def, i);
+             if (TREE_CODE (op) != INTEGER_CST
+                 && !operand_signed_p (op))
+               return false;
+           }
+
+         return true;
+       }
+    }
+
+  return !TYPE_UNSIGNED (TREE_TYPE (op));
+}
+
+/* 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-size-larger-than= or SIZE_MAX / 2.  */
+  tree maxobjsize = alloc_max_size ();
+
+  location_t loc = 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_size_larger_than_,
+                                  "argument %i value %qE is negative",
+                                  idx[i] + 1, args[i]);
+           }
+         else if (integer_zerop (args[i]))
+           {
+             /* Avoid issuing -Walloc-zero for allocation functions other
+                than __builtin_alloca that are declared with attribute
+                returns_nonnull because there's no portability risk.  This
+                avoids warning for such calls to libiberty's xmalloc and
+                friends.
+                Also avoid issuing the warning for calls to function named
+                "alloca".  */
+             if ((DECL_FUNCTION_CODE (fn) == BUILT_IN_ALLOCA
+                  && IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6)
+                 || (DECL_FUNCTION_CODE (fn) != BUILT_IN_ALLOCA
+                     && !lookup_attribute ("returns_nonnull",
+                                           TYPE_ATTRIBUTES (TREE_TYPE (fn)))))
+               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_size_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)
+           {
+             /* For an anti-range, if the type of the formal argument
+                is unsigned and the bounds of the range are of opposite
+                signs when interpreted as signed, check to see if the
+                type of the actual argument is signed.  If so, the lower
+                bound must be taken to be zero (rather than a large
+                positive value corresonding to the actual lower bound
+                interpreted as unsigned) and there is nothing else that
+                can be inferred from it.  */
+             --min;
+             ++max;
+             wide_int zero = wi::uhwi (0, TYPE_PRECISION (type));
+             if (TYPE_UNSIGNED (type)
+                 && wi::lts_p (zero, min) && wi::lts_p (max, zero)
+                 && operand_signed_p (args[i]))
+               continue;
+
+             argrange[i][0] = wide_int_to_tree (type, max);
+             argrange[i][1] = wide_int_to_tree (type, min);
+
+             /* 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_size_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_size_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_size_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])
+      && !integer_onep (argrange[0][0])
+      && !integer_onep (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_size_larger_than_,
+                            "product %<%E * %E%> of arguments %i and %i "
+                            "exceeds %<SIZE_MAX%>",
+                            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_size_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)
+    {
+      location_t fnloc = DECL_SOURCE_LOCATION (fn);
+
+      if (DECL_IS_BUILTIN (fn))
+       inform (loc,
+               "in a call to built-in allocation function %qD", fn);
+      else
+       inform (fnloc,
+               "in a call to allocation function %qD declared here", fn);
+    }
+}
+
 /* Issue an error if CALL_EXPR was flagged as requiring
    tall-call optimization.  */
 
@@ -1359,6 +1666,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 +1920,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);
     }
 }
 
index e14415646ca68a9fa9af4917125467471f83ec0b..3b0726345afb98430d380531b1c8bb757c67f61d 100644 (file)
@@ -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
index 02a34fb724dd15e489db38fe9fa47037d6a3bb8a..0c6123269a33250b4e6424017acdab9520a315e7 100644 (file)
@@ -257,6 +257,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-size-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
@@ -5099,6 +5100,27 @@ 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 (and in the case
+of @code{realloc} has been deprecated) relying on it may result in subtle
+portability bugs and should be avoided.
+
+@item -Walloc-size-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 / 2}.  The option argument @var{n}
+may end in one of the standard suffixes designating a multiple of bytes
+such as @code{kB} and @code{KiB} for kilobyte and kibibyte, respectively,
+@code{MB} and @code{MiB} for magabyte and mebibyte, and so on.
+@xref{Function Attributes}.
+
 @item -Walloca
 @opindex Wno-alloca
 @opindex Walloca
index 7fe0e96a05cda1b3ec860ad03275f2934cc0ae4c..80166f282a7c684d142041aa9b1465d621d81277 100644 (file)
@@ -1,3 +1,15 @@
+2016-12-08  Martin Sebor  <msebor@redhat.com>
+
+       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.
+       * gcc.dg/attr-alloc_size-8.c: New test.
+       * gcc.dg/attr-alloc_size-9.c: New test.
+       * gcc/testsuite/gcc.dg/errno-1.c: Adjust.
+
 2016-12-08  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        PR fortran/65173
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 (file)
index 0000000..284127f
--- /dev/null
@@ -0,0 +1,473 @@
+/* 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 default maximum object size (with -Walloc-size-larger-than
+   not explicitly specified).  */
+/* { 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 PTRDIFF_MAX __PTRDIFF_MAX__
+#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)
+#define SIZE_MAX    __SIZE_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+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, int i)
+{
+  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 (0, i));
+  sink (f_uchar_2 (n, 0));
+  sink (f_uchar_2 (i, 0));
+  sink (f_uchar_2 (1, n));
+  sink (f_uchar_2 (1, i));
+  sink (f_uchar_2 (n, 1));
+  sink (f_uchar_2 (i, 1));
+  sink (f_uchar_2 (max, n));
+  sink (f_uchar_2 (max, i));
+  sink (f_uchar_2 (n, max));
+  sink (f_uchar_2 (i, max));
+  sink (f_uchar_2 (n, n));
+  sink (f_uchar_2 (i, i));
+
+  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 ui, ptrdiff_t si)
+{
+  const ptrdiff_t smin = PTRDIFF_MIN;
+  const ptrdiff_t smax = PTRDIFF_MAX;
+  const size_t umax = SIZE_MAX;
+
+  sink (f_size_1 (ui));
+  sink (f_size_1 (si));
+
+  sink (f_size_1 (UR (0, 1)));
+  sink (f_size_1 (UR (0, umax - 1)));
+  sink (f_size_1 (UR (1, umax - 1)));
+  sink (f_size_1 (UR (1, umax)));
+
+  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, umax / 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, umax / 2)));
+
+  sink (f_size_2 (ui, ui));
+  sink (f_size_2 (si, si));
+  sink (f_size_2 (ui, umax / 2));
+  sink (f_size_2 (si, umax / 2));
+  sink (f_size_2 (umax / 2, ui));
+  sink (f_size_2 (umax / 2, si));
+
+  sink (f_size_2 (UR (0, 1), umax));   /* { dg-warning "argument 2 value .\[0-9\]+\[lu\]*. exceeds maximum object size " } */
+  sink (f_size_2 (UR (0, 1), umax / 2));
+  sink (f_size_2 (UR (0, umax / 2), umax / 2));
+
+  sink (f_size_2 (UR (umax / 2 + 1, umax / 2 + 2), ui));  /* { dg-warning "argument 1 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */
+  sink (f_size_2 (ui, UR (umax / 2 + 1, umax / 2 + 2)));  /* { dg-warning "argument 2 range \\\[\[0-9\]+\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size " } */
+  sink (f_size_2 (UR (umax / 2 + 1, umax), UR (umax / 2 + 1, umax)));  /* { 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 } */
+
+  sink (f_size_2 (SR (smin, 1), 1));
+  sink (f_size_2 (SR (smin, 1), umax / 2));
+  sink (f_size_2 (SR (-1, smax), 1));
+  sink (f_size_2 (SR (-1, smax), umax / 2));
+  sink (f_size_2 (SR (-1, 1), 1));
+  sink (f_size_2 (SR (-1, 1), umax / 2));
+  sink (f_size_2 (SR (-9, 9), 1));
+  sink (f_size_2 (SR (-9, 9), umax / 2));
+}
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 (file)
index 0000000..0a07d6e
--- /dev/null
@@ -0,0 +1,191 @@
+/* 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 specified by -Walloc-size-larger-than.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Walloc-size-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 (file)
index 0000000..f9884ed
--- /dev/null
@@ -0,0 +1,234 @@
+/* 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* f_size_1_nonnull (size_t)
+     ALLOC_SIZE (1)  __attribute__ ((returns_nonnull));
+void* f_size_2_nonnull (size_t, size_t)
+     ALLOC_SIZE (1, 2) __attribute__ ((returns_nonnull));
+
+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" } */
+}
+
+/* Verify that calls to allocation function decorated with attribute
+   returns_nonnull don't cause warnings (unlike functions like malloc
+   that can return null in this case there's nothing to warn about
+   because a returns_nonnull function guarantees success).  */
+
+void
+test_size_nonnull (size_t n)
+{
+  sink (f_size_1_nonnull (0));
+  sink (f_size_2_nonnull (0, 1));
+  sink (f_size_2_nonnull (1, 0));
+  sink (f_size_2_nonnull (n, 0));
+  sink (f_size_2_nonnull (0, n));
+
+  sink (f_size_1_nonnull (n));
+  n = 0;
+  sink (f_size_1_nonnull (n));
+  sink (f_size_2_nonnull (1, n));
+}
+
+/* Verify that call to plain alloca(0) is not diagnosed.  */
+
+void
+test_alloca (size_t n)
+{
+  extern void* alloca (size_t);
+
+  alloca (0);
+}
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 (file)
index 0000000..38890b6
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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-size-larger-than=12345" } */
+
+#define MAXOBJSZ  12345
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*);
+
+
+void test_lit (char *p, char *q)
+{
+  sink (__builtin_aligned_alloc (MAXOBJSZ, 1));
+  sink (__builtin_aligned_alloc (MAXOBJSZ + 1, 1));   /* { dg-warning "argument 1 value .12346\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_alloca (MAXOBJSZ));
+  sink (__builtin_alloca (MAXOBJSZ + 2));   /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_calloc (MAXOBJSZ, 1));
+  sink (__builtin_calloc (1, MAXOBJSZ));
+
+  /* Verify that the signed to unsigned conversion below doesn't cause
+     a warning.  */
+  sink (__builtin_calloc (p - q, 1));
+  sink (__builtin_calloc (1, q - p));
+  sink (__builtin_calloc (p - q, MAXOBJSZ));
+  sink (__builtin_calloc (MAXOBJSZ, q - p));
+
+  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));
+  sink (__builtin_malloc (MAXOBJSZ + 3));   /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_realloc (p, MAXOBJSZ));
+  sink (__builtin_realloc (p, MAXOBJSZ + 4));  /* { dg-warning "argument 2 value .12349\[lu\]*. exceeds maximum object size 12345" } */
+}
+
+
+enum { max = MAXOBJSZ };
+
+void test_cst (char *p, char *q)
+{
+  sink (__builtin_aligned_alloc (max, 1));
+  sink (__builtin_aligned_alloc (max + 1, 1));   /* { dg-warning "argument 1 value .12346\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_alloca (max));
+  sink (__builtin_alloca (max + 2));   /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_calloc (max, 1));
+  sink (__builtin_calloc (1, max));
+
+  /* Verify that the signed to unsigned conversion below doesn't cause
+     a warning.  */
+  sink (__builtin_calloc (p - q, 1));
+  sink (__builtin_calloc (1, q - p));
+  sink (__builtin_calloc (p - q, max));
+  sink (__builtin_calloc (max, q - p));
+
+  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));
+  sink (__builtin_malloc (max + 3));   /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_realloc (p, max));
+  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 (file)
index 0000000..d6e618d
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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-size-larger-than=maximum.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Walloc-size-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));
+  sink (__builtin_aligned_alloc (max + 1, 1));   /* { dg-warning "argument 1 value .12346\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_alloca (max));
+  sink (__builtin_alloca (max + 2));   /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_calloc (1, max));
+  sink (__builtin_calloc (max, 1));
+
+  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));
+  sink (__builtin_malloc (max + 3));   /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_realloc (p, max));
+  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));
+  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));
+  sink (__builtin_alloca (range + 2));   /* { dg-warning "argument 1 range \\\[12347\[lu\]*, \[0-9\]+\[lu\]*\\\] exceeds maximum object size 12345" } */
+
+  sink (__builtin_calloc (range, 1));
+  sink (__builtin_calloc (1, range));
+
+  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));
+  sink (__builtin_malloc (range + 3));   /* { dg-warning "argument 1 range \\\[12348\[lu\]*, 24692\[lu\]*\\\] exceeds maximum object size 12345" } */
+
+  sink (__builtin_realloc (p, range));
+  sink (__builtin_realloc (p, range + 4));  /* { dg-warning "argument 2 range \\\[12349\[lu\]*, 24693\[lu\]*\\\] exceeds maximum object size 12345" } */
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-8.c b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
new file mode 100644 (file)
index 0000000..6282a46
--- /dev/null
@@ -0,0 +1,61 @@
+/* PR c/78284 - warn on malloc with very large arguments
+   Test to exercise the interaction of the -Walloca-larger-than,
+   -Wvla-larger-than, and -Walloc-size-larger-than options.  The former
+   two more specific options override the more general latter option.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Walloc-size-larger-than=123 -Walloca-larger-than=234 -Wvla-larger-than=345" } */
+
+#define SIZE_MAX   __SIZE_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*);
+
+size_t alloc_size_limit (void)
+{
+  return 123;
+}
+
+size_t alloca_limit (void)
+{
+  return 234;
+}
+
+size_t vla_limit (void)
+{
+  return 345;
+}
+
+void test_alloca (void)
+{
+  void *p;
+
+  /* No warning should be issued for the following call because the more
+     permissive alloca limit overrides the stricter alloc_size limit.  */
+  p = __builtin_alloca (alloca_limit ());
+  sink (p);
+
+  p = __builtin_alloca (alloca_limit () + 1);   /* { dg-warning "argument to .alloca. is too large" } */
+  sink (p);
+}
+
+void test_vla (void)
+{
+  /* Same as above, no warning should be issued here because the more
+     permissive VLA limit overrides the stricter alloc_size limit.  */
+  char vla1 [vla_limit ()];
+  sink (vla1);
+
+  char vla2 [vla_limit () + 1];   /* { dg-warning "argument to variable-length array is too large" } */
+  sink (vla2);
+}
+
+void test_malloc (void)
+{
+  void *p;
+  p = __builtin_malloc (alloc_size_limit ());
+  sink (p);
+
+  p = __builtin_malloc (alloc_size_limit () + 1);   /* { dg-warning "argument 1 value .124\[lu\]*. exceeds maximum object size 123" } */
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-9.c b/gcc/testsuite/gcc.dg/attr-alloc_size-9.c
new file mode 100644 (file)
index 0000000..66765fd
--- /dev/null
@@ -0,0 +1,30 @@
+/* PR c/78284 - warn on malloc with very large arguments
+   Test verifying that the built-in allocation functions are declared
+   with attribute malloc.  This means that the pointer they return
+   can be assumed not to alias any other valid pointer.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void sink (void*);
+
+extern int x;
+
+#define TEST(call)                             \
+  do {                                         \
+    p = call;                                  \
+    x = 123;                                   \
+    *(int*)p = 456;                            \
+    (x == 123) ? (void)0 : __builtin_abort (); \
+    sink (p);                                  \
+  } while (0)
+
+void test (void *p, unsigned n)
+{
+  TEST (__builtin_aligned_alloc (n, 8));
+  TEST (__builtin_alloca (n));
+  TEST (__builtin_calloc (4, n));
+  TEST (__builtin_malloc (n));
+  TEST (__builtin_realloc (p, n + 1));
+}
+
+/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
index 46236ef5123aad8969f71f88bd877858d31566c5..0dfb00087dee04cd5ff1e916149c0c13c9b87b20 100644 (file)
@@ -10,6 +10,7 @@ int main()
 {
   void * volatile p;
   errno = 0;
+  /* The malloc call below may cause a -Walloc-size-larger-than warning.  */
   p = malloc (-1);
   if (errno != 0)
     do_not_optimize_away ();
@@ -17,3 +18,4 @@ int main()
 }
 
 /* { dg-final { scan-assembler "do_not_optimize_away" } } */
+/* { dg-prune-output "exceeds maximum object size" } */