public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
@ 2016-11-14  3:19 Martin Sebor
  2016-11-14 20:34 ` Eric Gallager
  2016-11-16 17:20 ` Martin Sebor
  0 siblings, 2 replies; 16+ messages in thread
From: Martin Sebor @ 2016-11-14  3:19 UTC (permalink / raw)
  To: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 1637 bytes --]

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?

[-- Attachment #2: gcc-78284.diff --]
[-- Type: text/x-patch, Size: 14048 bytes --]

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/77531
	PR c/78284
	* c.opt (-Walloc-zero, -Walloc-larger-than): New options.

gcc/testsuite/ChangeLog:

	PR c/77531
	PR c/78284
	* gcc.dg/attr-alloc_size-3.c: New test.

gcc/ChangeLog:

	PR c/77531
	PR c/78284
	* calls.c (alloc_max_size): New function.
	(maybe_warn_alloc_args_overflow): Same.
	(initialize_argument_information): Diagnose overflow in functions
	declared with attaribute alloc_size.
	* doc/invoke.texi (Warning Options): Document -Walloc-zero and
	-Walloc-larger-than.

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=<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 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=<number> Warn on unbounded uses of
diff --git a/gcc/calls.c b/gcc/calls.c
index c916e07..dfeb5fe 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -48,6 +48,8 @@ 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"
 
 
@@ -1181,6 +1183,192 @@ 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.  */
+
+static void
+maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
+{
+  if (!args[0])
+    return;
+
+  bool warned = false;
+
+  if (!args[1])
+    args[1] = size_one_node;
+
+  bool range[2] = { false, false };
+
+  /* Determine the minimum value of the argument.  */
+  for (unsigned i = 0; i != 2; ++i)
+    {
+      if (TREE_CODE (args[i]) == SSA_NAME)
+	{
+	  tree type = TREE_TYPE (args[i]);
+	  wide_int zero = build_int_cst (type, 0);
+	  signop sgn = TYPE_SIGN (type);
+
+	  wide_int min, max;
+	  value_range_type range_type = get_range_info (args[i], &min, &max);
+	  if (range_type == VR_RANGE)
+	    {
+	      if (wi::lt_p (min, zero, sgn) && wi::ge_p (max, zero, sgn))
+		continue;
+
+	      args[i] = wide_int_to_tree (type, min);
+	    }
+	  else if (range_type == VR_ANTI_RANGE)
+	    {
+	      if (wi::lt_p (min - 1, zero, sgn) && wi::ge_p (max + 1, zero, sgn))
+		continue;
+	      args[i] = wide_int_to_tree (type, max + 1);
+	    }
+	  else
+	    args[i] = size_one_node;
+
+	  range[i] = true;
+
+	  if (integer_zerop (args[i]))
+	    args[i] = size_one_node;
+	}
+    }
+
+  tree maxobjsize = alloc_max_size ();
+
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (tree_fits_uhwi_p (args[0]) && tree_fits_uhwi_p (args[1])
+      && !integer_zerop (args[0]) && !integer_zerop (args[1])
+      && !integer_onep (args[0]) && !integer_onep (args[1]))
+    {
+      /* 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 (args[0], szprec);
+      wide_int y = wi::to_wide (args[1], szprec);
+
+      bool vflow;
+      wide_int prod = wi::umul (x, y, &vflow);
+
+      if (vflow)
+	warned = warning_at (loc, OPT_Walloc_larger_than_,
+			     "product of arguments %i and %i results "
+			     "in unsigned overflow: %<%E * %E%>",
+			     idx[0] + 1, idx[1] + 1, args[0], args[1]);
+      else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod))
+	warned = warning_at (loc, OPT_Walloc_larger_than_,
+			     "product of arguments %i and %i exceeds "
+			     "maximum object size %E: %<%E * %E%>",
+			     idx[0] + 1, idx[1] + 1,
+			     maxobjsize, args[0], args[1]);
+    }
+  else
+    {
+      /* This is either a function decorated with attribute alloc_size (X),
+	 or one that takes two allocation arguments like realloc and one
+	 of the arguments is equal to 0, or to 1, or it value is unknown.  */
+      for (unsigned i = 0; i != 2; ++i)
+	{
+	  if (tree_fits_uhwi_p (args[i]))
+	    if (tree_int_cst_lt (maxobjsize, args[i]))
+	      {
+		warned = warning_at (loc, OPT_Walloc_larger_than_,
+				     "argument %i value %qE exceeds maximum "
+				     "object size %E",
+				     idx[i] + 1, args[i], maxobjsize);
+		break;
+	      }
+	  if (!range[i])
+	    {
+	      if (integer_zerop (args[i]))
+		{
+		  warned = warning_at (loc, OPT_Walloc_zero,
+				       "argument %i value is zero",
+				       idx[i] + 1);
+		  break;
+		}
+	      else if (!tree_expr_nonnegative_p (args[i]))
+		{
+		  warned = warning_at (loc, OPT_Walloc_larger_than_,
+				       "argument %i value %qE is negative",
+				       idx[i] + 1, args[i]);
+		  break;
+		}
+	    }
+	}
+    }
+
+  if (warned)
+    inform (DECL_SOURCE_LOCATION (fn),
+	    "in a call to allocation function %qD declared here", fn);
+}
+
 /* Issue an error if CALL_EXPR was flagged as requiring
    tall-call optimization.  */
 
@@ -1280,6 +1468,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	    j--;
 	  }
       }
+
     argpos = 0;
     FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
       {
@@ -1349,6 +1538,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	  }
 	else
 	  args[j].tree_value = arg;
+
 	j--;
 	argpos++;
       }
@@ -1359,6 +1549,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,7 +1803,17 @@ 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;
     }
+
+  /* Check the arguments of functions decorated with attribute alloc_size. */
+  maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx);
 }
 
 /* Update ARGS_SIZE to contain the total size for the argument block.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 76b8540..2ff0b65 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,17 @@ 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 that specify zero bytes.  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 computation of the size would overflow.  @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..adf9333
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-3.c
@@ -0,0 +1,101 @@
+/* { 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 * 2 + 1)
+
+#define LONG_MAX   __LONG_MAX__
+#define LONG_MIN   (-LONG_MAX - 1L)
+#define ULONG_MAX  (LONG_MAX * 2 + 1)
+
+#define LLONG_MAX  __LLONG_MAX__
+#define LLONG_MIN  (-LLONG_MAX - 1LL)
+#define ULLONG_MAX (ULLONG_MAX * 2 + 1)
+
+#define ALLOC_SIZE(...) __attribute__ ((alloc_size (__VA_ARGS__)))
+
+void* f_uchar_1 (unsigned char) ALLOC_SIZE (1);
+void* f_schar_1 (signed char) ALLOC_SIZE (1);
+
+void* f_ushrt_1 (unsigned short) ALLOC_SIZE (1);
+void* f_shrt_1 (signed short) ALLOC_SIZE (1);
+
+void* f_uint_1 (unsigned int) ALLOC_SIZE (1);
+void* f_sint_1 (signed int) ALLOC_SIZE (1);
+
+void* f_ulong_1 (unsigned long) ALLOC_SIZE (1);
+void* f_slong_1 (signed long) ALLOC_SIZE (1);
+
+void* f_ullong_1 (unsigned long long) ALLOC_SIZE (1);
+void* f_sllong_1 (signed long long) ALLOC_SIZE (1);
+
+void sink (void*);
+
+void
+test_uchar (unsigned char n)
+{
+  sink (f_uchar_1 (UCHAR_MAX));
+  sink (f_uchar_1 (1));
+  sink (f_uchar_1 (0));  /* { dg-warning "argument 1 value is zero" } */
+  sink (f_uchar_1 (n));
+}
+
+void
+test_schar (signed char n)
+{
+  sink (f_schar_1 (SCHAR_MAX));
+  sink (f_schar_1 (SCHAR_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 (n));
+}
+
+void
+test_ushrt (unsigned short n)
+{
+  sink (f_ushrt_1 (USHRT_MAX));
+  sink (f_ushrt_1 (1));
+  sink (f_ushrt_1 (0));  /* { dg-warning "argument 1 value is zero" } */
+  sink (f_ushrt_1 (n));
+}
+
+void
+test_sshrt (signed short n)
+{
+  sink (f_shrt_1 (SHRT_MAX));
+  sink (f_shrt_1 (SHRT_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));          /* { dg-warning "argument 1 value is zero" } */
+  sink (f_shrt_1 (1));
+  sink (f_shrt_1 (n));
+}
+
+void
+test_uint (unsigned int n)
+{
+  sink (f_uint_1 (UINT_MAX));
+  sink (f_uint_1 (1));
+  sink (f_uint_1 (0));  /* { dg-warning "argument 1 value is zero" } */
+  sink (f_uint_1 (n));
+}
+
+void
+test_sint (signed short n)
+{
+  sink (f_int_1 (INT_MAX));
+  sink (f_int_1 (INT_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));          /* { dg-warning "argument 1 value is zero" } */
+  sink (f_int_1 (1));
+  sink (f_int_1 (n));
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-14  3:19 [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284) Martin Sebor
@ 2016-11-14 20:34 ` Eric Gallager
  2016-11-16 17:06   ` Martin Sebor
  2016-11-16 17:20 ` Martin Sebor
  1 sibling, 1 reply; 16+ messages in thread
From: Eric Gallager @ 2016-11-14 20:34 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On 11/13/16, Martin Sebor <msebor@gmail.com> 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?
>


-Walloc-larger-than looks way too similar to -Walloca-larger-than; at
first I was confused as to why you were adding the same flag again
until I spotted the one letter difference. Maybe come up with a name
that looks more distinct? Just something to bikeshed about.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-14 20:34 ` Eric Gallager
@ 2016-11-16 17:06   ` Martin Sebor
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Sebor @ 2016-11-16 17:06 UTC (permalink / raw)
  To: Eric Gallager; +Cc: Gcc Patch List

On 11/14/2016 01:34 PM, Eric Gallager wrote:
> On 11/13/16, Martin Sebor <msebor@gmail.com> 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?
>>
>
>
> -Walloc-larger-than looks way too similar to -Walloca-larger-than; at
> first I was confused as to why you were adding the same flag again
> until I spotted the one letter difference. Maybe come up with a name
> that looks more distinct? Just something to bikeshed about.

I agree.  I've renamed the option to -Walloc-size-larger-than.
I think that works because it goes along with attribute alloc_size.
I'm about to post an updated patch with that change (among others).

Thanks
Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-14  3:19 [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284) Martin Sebor
  2016-11-14 20:34 ` Eric Gallager
@ 2016-11-16 17:20 ` Martin Sebor
  2016-11-18  0:15   ` Martin Sebor
  1 sibling, 1 reply; 16+ messages in thread
From: Martin Sebor @ 2016-11-16 17:20 UTC (permalink / raw)
  To: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 2953 bytes --]

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?



[-- Attachment #2: gcc-78284.diff --]
[-- Type: text/x-patch, Size: 58979 bytes --]

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=<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 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=<number> 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 %<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_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)))


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-16 17:20 ` Martin Sebor
@ 2016-11-18  0:15   ` Martin Sebor
  2016-11-20  0:56     ` Martin Sebor
  0 siblings, 1 reply; 16+ messages in thread
From: Martin Sebor @ 2016-11-18  0:15 UTC (permalink / raw)
  To: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 3723 bytes --]

Attached is an update to the patch that avoids duplicating the
-Walloca-larger-than warnings.  This version also avoids warning
for calls with zero allocation size to functions declared with
the returns_nonnull attribute (like libiberty's xmalloc).  Since
such functions cannot return null there's no portability issue
to worry/warn about.

When applied along with the patch to avoid calling alloca(0)
in GCC posted earlier today (link below) this version bootstraps
and passes all tests on x86_64.  It also builds Binutils 2.27 and
the Linux kernel with no new warnings.

   https://gcc.gnu.org/ml/gcc-patches/2016-11/msg01838.html

Martin

On 11/16/2016 10:19 AM, Martin Sebor wrote:
> 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?
>
>


[-- Attachment #2: gcc-78284.diff --]
[-- Type: text/x-patch, Size: 64575 bytes --]

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/77531
	PR c/78284
	* c.opt (-Walloc-zero, -Walloc-size-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-size-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.
	* 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.

diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def
index 8dc59c9..458ea58 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_MALLOC_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..d62898d 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,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));
 
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..75b8498 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-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 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=<number> Warn on unbounded uses of
diff --git a/gcc/calls.c b/gcc/calls.c
index c916e07..3ae017c 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,250 @@ 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-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;
+}
+
+/* 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 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.  */
+	      if (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)
+	    {
+	      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_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 (fnloc != UNKNOWN_LOCATION)
+	inform (fnloc,
+		"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 +1605,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 +1859,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..c93c5a3 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-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
@@ -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-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}.
+@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..8b4f0d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-3.c
@@ -0,0 +1,448 @@
+/* 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..2d30664
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-4.c
@@ -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.  */
+/* { 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
index 0000000..09814c9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
@@ -0,0 +1,224 @@
+/* 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));
+}
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..a8a131e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-6.c
@@ -0,0 +1,59 @@
+/* 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 (void *p)
+{
+  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));
+
+  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 (void *p)
+{
+  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));
+
+  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
index 0000000..d6e618d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
@@ -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
index 0000000..6282a46
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
@@ -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
index 0000000..66765fd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-9.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/errno-1.c b/gcc/testsuite/gcc.dg/errno-1.c
index 46236ef..0dfb000 100644
--- a/gcc/testsuite/gcc.dg/errno-1.c
+++ b/gcc/testsuite/gcc.dg/errno-1.c
@@ -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" } */

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-18  0:15   ` Martin Sebor
@ 2016-11-20  0:56     ` Martin Sebor
  2016-12-02 23:28       ` Jeff Law
                         ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Martin Sebor @ 2016-11-20  0:56 UTC (permalink / raw)
  To: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 6936 bytes --]

The attached update is an attempt to incorporate the feedback
I received last week during the discussion of the prerequisite
patch to avoid calling alloca(0)​.

The important changes are:

1) Add attribute returns_nonnull to __builtin_alloca.
2) Prevent calls to alloca(0) from triggering the -Walloc-zero
    warning when the call is to the function named alloca (as
    opposed to one made explicitly to __builtin_alloca).  I don't
    think this special treatment is necessary but a concern was
    voiced that short of disabling the warning altogether there
    was no way to suppress the warning for individual calls to
    alloca without adding 1 to the size, and that the addition
    might adversely affect performance.
3) Fix a bug in the handling of certain calls to calloc(a, b)
    with signed arguments.

Besides bootstrapping GCC and running the test suite I tested
this patch by compiling Binutils 2.27, Busybox 1.25.1, the trunk
of Glibc, and the Linux kernel with no instances of either of
the new warnings.  (GCC requires the alloca(0) patch to fix
the -Walloc-zero warnings.)

Since there has been quite a bit of discussion in response to
the related and prerequisite alloca(0) patch for GCC let me
summarize the rationale for this patch and the two new warnings
it adds:

1) -Walloc-larger-than=max (enabled by default with SIZE_MAX / 2)
    Calls to calloc(a, b) where the product (a * b) overflows are
    a source of bugs.  calloc is expected to fail in this case but
    implementations have been known to get this wrong.  See [1].

    Calls to allocation functions such as malloc with a very large
    argument can be the result of an incorrect computation or
    conversion from a negative argument, or integer overflow.
    Such calls normally fail at runtime but malloc error handling
    is often poorly exercised and the error handling code buggy.
    Detecting these early helps avoid these problems.

2) -Walloc-zero (enabled with -Wextra)
    Zero allocations are a notorious source bugs and security
    vulnerabilities, not only because the result of such calls
    is implementation-defined (some return null, others a non-null
    pointer to a zero-sized object), but also because such calls
    are sometimes the result of integer overflow.  See [2].
    In addition, C11 has deprecated calling realloc(0, p) in
    response to defect report 400 [3].
    Finally, calling alloca(0) is dangerous because like malloc(0),
    the result may be either null or non-null, but unlike malloc,
    when non-null, the pointer need not be distinct from others.
    This warning is analogous to the Clang static analyzer warning
    for zero-size allocation calls.

[1] RUS-CERT Advisory 2002-08:02 – Flaw in calloc and similar
routines
https://cert.uni-stuttgart.de/ticker/advisories/calloc.html

[2] Zero-sized heap allocations vulnerability analysis
https://www.usenix.org/legacy/event/woot10/tech/full_papers/Vanegue.pdf

[3] DR 400 - realloc with size zero problems
http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm#dr_400

On 11/17/2016 05:15 PM, Martin Sebor wrote:
> Attached is an update to the patch that avoids duplicating the
> -Walloca-larger-than warnings.  This version also avoids warning
> for calls with zero allocation size to functions declared with
> the returns_nonnull attribute (like libiberty's xmalloc).  Since
> such functions cannot return null there's no portability issue
> to worry/warn about.
>
> When applied along with the patch to avoid calling alloca(0)
> in GCC posted earlier today (link below) this version bootstraps
> and passes all tests on x86_64.  It also builds Binutils 2.27 and
> the Linux kernel with no new warnings.
>
>   https://gcc.gnu.org/ml/gcc-patches/2016-11/msg01838.html
>
> Martin
>
> On 11/16/2016 10:19 AM, Martin Sebor wrote:
>> 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?
>>
>>
>


[-- Attachment #2: gcc-78284.diff --]
[-- Type: text/x-patch, Size: 68953 bytes --]

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/77531
	PR c/78284
	* c.opt (-Walloc-zero, -Walloc-size-larger-than): New options.

gcc/ChangeLog:

	PR c/77531
	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/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.
	* 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.

diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def
index 8dc59c9..c035134 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")
@@ -108,6 +109,7 @@ DEF_ATTR_IDENT (ATTR_TYPEGENERIC, "type generic")
 DEF_ATTR_IDENT (ATTR_TM_REGPARM, "*tm regparm")
 DEF_ATTR_IDENT (ATTR_TM_TMPURE, "transaction_pure")
 DEF_ATTR_IDENT (ATTR_RETURNS_TWICE, "returns_twice")
+DEF_ATTR_IDENT (ATTR_RETURNS_NONNULL, "returns_nonnull")
 
 DEF_ATTR_TREE_LIST (ATTR_NOVOPS_LIST, ATTR_NOVOPS, ATTR_NULL, ATTR_NULL)
 
@@ -150,6 +152,26 @@ 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 malloc and realloc 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_MALLOC_NOTHROW_LIST)
+DEF_ATTR_TREE_LIST (ATTR_MALLOC_SIZE_1_NOTHROW_LEAF_LIST, ATTR_ALLOC_SIZE, \
+		        ATTR_LIST_1, ATTR_MALLOC_NOTHROW_LEAF_LIST)
+/* Alloca is just like malloc except that it never returns null.  */
+DEF_ATTR_TREE_LIST (ATTR_ALLOCA_SIZE_1_NOTHROW_LEAF_LIST, ATTR_RETURNS_NONNULL,
+		    ATTR_NULL, ATTR_MALLOC_SIZE_1_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 1316c27..92562ed 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -4319,12 +4319,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));
@@ -4332,6 +4332,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));
 
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 219feeb..f2140a0 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_ALLOCA_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 722d380..874332a 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -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 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=<number> Warn on unbounded uses of
diff --git a/gcc/calls.c b/gcc/calls.c
index c916e07..05e6e09 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,310 @@ 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-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 +1665,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 +1919,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 e6c3dc2..9f10ad6 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-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
@@ -4991,6 +4992,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-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}.
+@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..284127f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-3.c
@@ -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
index 0000000..0a07d6e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-4.c
@@ -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
index 0000000..f9884ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
@@ -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
index 0000000..38890b6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-6.c
@@ -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
index 0000000..d6e618d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
@@ -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
index 0000000..6282a46
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
@@ -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
index 0000000..66765fd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-9.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/errno-1.c b/gcc/testsuite/gcc.dg/errno-1.c
index 46236ef..0dfb000 100644
--- a/gcc/testsuite/gcc.dg/errno-1.c
+++ b/gcc/testsuite/gcc.dg/errno-1.c
@@ -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" } */

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-20  0:56     ` Martin Sebor
@ 2016-12-02 23:28       ` Jeff Law
  2016-12-07 22:04       ` Jeff Law
  2016-12-09 13:26       ` Andreas Schwab
  2 siblings, 0 replies; 16+ messages in thread
From: Jeff Law @ 2016-12-02 23:28 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 11/19/2016 05:55 PM, Martin Sebor wrote:
> The attached update is an attempt to incorporate the feedback
> I received last week during the discussion of the prerequisite
> patch to avoid calling alloca(0)​.
>
> The important changes are:
>
> 1) Add attribute returns_nonnull to __builtin_alloca.
This should be broken out into its own change.  I can't see a good 
reason offhand why this has to be tied up with the overflow checking.

I'll go ahead and pre-approve this part. (builtin-attrs.def/builtins.def 
changes).  If they can be installed separately, please do.

I won't have the time to look at the rest of this today.

jeff


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-20  0:56     ` Martin Sebor
  2016-12-02 23:28       ` Jeff Law
@ 2016-12-07 22:04       ` Jeff Law
  2016-12-08 23:55         ` Martin Sebor
  2016-12-09 13:26       ` Andreas Schwab
  2 siblings, 1 reply; 16+ messages in thread
From: Jeff Law @ 2016-12-07 22:04 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 11/19/2016 05:55 PM, Martin Sebor wrote:

>
> gcc-78284.diff
>
>
> 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/77531
> 	PR c/78284
> 	* c.opt (-Walloc-zero, -Walloc-size-larger-than): New options.
>
> gcc/ChangeLog:
>
> 	PR c/77531
> 	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/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.
> 	* 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.
>

> diff --git a/gcc/calls.c b/gcc/calls.c
> index c916e07..05e6e09 100644
> --- a/gcc/calls.c
> +++ b/gcc/calls.c
> @@ -1181,6 +1183,310 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals)
>        }
>  }
>
> +static tree alloc_object_size_limit;
Is this object live across GCC passes (I think it is)?  If so, then it 
needs a GTY marker.





> +	      /* 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.  */
How unfortunate :(


> +	  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.  */
s/corresonding/corresponding/



>
>
>
> +@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}.
So I think we should in the immediate term not enable this in Wextra. 
However, I think for gcc-8 we should revisit after fixing GCC to be 
cleaner WRT alloc-zero.

So disable alloc-zero by default, comment typo and potentially adding 
the GTY marker to alloc_object_size_limit and this is OK.

Jeff


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-12-07 22:04       ` Jeff Law
@ 2016-12-08 23:55         ` Martin Sebor
  2016-12-09  9:59           ` Christophe Lyon
  0 siblings, 1 reply; 16+ messages in thread
From: Martin Sebor @ 2016-12-08 23:55 UTC (permalink / raw)
  To: Jeff Law, Gcc Patch List

>> +enabled with @option{-Wextra}.
> So I think we should in the immediate term not enable this in Wextra.
> However, I think for gcc-8 we should revisit after fixing GCC to be
> cleaner WRT alloc-zero.
>
> So disable alloc-zero by default, comment typo and potentially adding
> the GTY marker to alloc_object_size_limit and this is OK.

Thanks.  Committed in r243470 with the changes above.

Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-12-08 23:55         ` Martin Sebor
@ 2016-12-09  9:59           ` Christophe Lyon
  2016-12-09 17:39             ` Martin Sebor
  0 siblings, 1 reply; 16+ messages in thread
From: Christophe Lyon @ 2016-12-09  9:59 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jeff Law, Gcc Patch List

On 9 December 2016 at 00:54, Martin Sebor <msebor@gmail.com> wrote:
>>> +enabled with @option{-Wextra}.
>>
>> So I think we should in the immediate term not enable this in Wextra.
>> However, I think for gcc-8 we should revisit after fixing GCC to be
>> cleaner WRT alloc-zero.
>>
>> So disable alloc-zero by default, comment typo and potentially adding
>> the GTY marker to alloc_object_size_limit and this is OK.
>
>
> Thanks.  Committed in r243470 with the changes above.
>
> Martin

Hi,

The new test attr-alloc_size-3.c fails on arm* targets:
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:
In function 'test_schar_cst':
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:175:3:
warning: argument 1 value '-128' is negative
[-Walloc-size-larger-than=]
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:42:7:
note: in a call to allocation function 'f_schar_1' declared here
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:176:3:
warning: argument 1 value '-1' is negative [-Walloc-size-larger-than=]
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:42:7:
note: in a call to allocation function 'f_schar_1' declared here
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:181:3:
warning: argument 2 value '-128' is negative
[-Walloc-size-larger-than=]
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:43:7:
note: in a call to allocation function 'f_schar_2' declared here
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:182:3:
warning: argument 1 value '-128' is negative
[-Walloc-size-larger-than=]
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:183:3:
warning: argument 2 value '-1' is negative [-Walloc-size-larger-than=]
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:43:7:
note: in a call to allocation function 'f_schar_2' declared here
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:184:3:
warning: argument 1 value '-1' is negative [-Walloc-size-larger-than=]

[....]

Christophe

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-20  0:56     ` Martin Sebor
  2016-12-02 23:28       ` Jeff Law
  2016-12-07 22:04       ` Jeff Law
@ 2016-12-09 13:26       ` Andreas Schwab
  2016-12-09 17:44         ` Martin Sebor
  2 siblings, 1 reply; 16+ messages in thread
From: Andreas Schwab @ 2016-12-09 13:26 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

FAIL: gcc.dg/attr-alloc_size-3.c  (test for warnings, line 445)
FAIL: gcc.dg/attr-alloc_size-3.c (test for excess errors)
Excess errors:
/daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:244:3: warning: product '65535 * 65535' of arguments 1 and 2 exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
/daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:298:3: warning: argument 1 value '4294967294' exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
/daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:299:3: warning: argument 1 value '4294967295' exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-12-09  9:59           ` Christophe Lyon
@ 2016-12-09 17:39             ` Martin Sebor
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Sebor @ 2016-12-09 17:39 UTC (permalink / raw)
  To: Christophe Lyon; +Cc: Jeff Law, Gcc Patch List

On 12/09/2016 02:59 AM, Christophe Lyon wrote:
> On 9 December 2016 at 00:54, Martin Sebor <msebor@gmail.com> wrote:
>>>> +enabled with @option{-Wextra}.
>>>
>>> So I think we should in the immediate term not enable this in Wextra.
>>> However, I think for gcc-8 we should revisit after fixing GCC to be
>>> cleaner WRT alloc-zero.
>>>
>>> So disable alloc-zero by default, comment typo and potentially adding
>>> the GTY marker to alloc_object_size_limit and this is OK.
>>
>>
>> Thanks.  Committed in r243470 with the changes above.
>>
>> Martin
>
> Hi,
>
> The new test attr-alloc_size-3.c fails on arm* targets:
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:
> In function 'test_schar_cst':
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:175:3:
> warning: argument 1 value '-128' is negative
> [-Walloc-size-larger-than=]
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:42:7:
> note: in a call to allocation function 'f_schar_1' declared here
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:176:3:
> warning: argument 1 value '-1' is negative [-Walloc-size-larger-than=]
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:42:7:
> note: in a call to allocation function 'f_schar_1' declared here
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:181:3:
> warning: argument 2 value '-128' is negative
> [-Walloc-size-larger-than=]
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:43:7:
> note: in a call to allocation function 'f_schar_2' declared here
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:182:3:
> warning: argument 1 value '-128' is negative
> [-Walloc-size-larger-than=]
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:183:3:
> warning: argument 2 value '-1' is negative [-Walloc-size-larger-than=]
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:43:7:
> note: in a call to allocation function 'f_schar_2' declared here
> /aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:184:3:
> warning: argument 1 value '-1' is negative [-Walloc-size-larger-than=]

Thanks for the heads up!  I've tweaked the test to avoid these
failures (tested with a arm-unknown-linux-gnueabi cross-compiler)
and committed r243497.

Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-12-09 13:26       ` Andreas Schwab
@ 2016-12-09 17:44         ` Martin Sebor
  2016-12-09 19:44           ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Martin Sebor @ 2016-12-09 17:44 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: Gcc Patch List

On 12/09/2016 06:26 AM, Andreas Schwab wrote:
> FAIL: gcc.dg/attr-alloc_size-3.c  (test for warnings, line 445)
> FAIL: gcc.dg/attr-alloc_size-3.c (test for excess errors)
> Excess errors:
> /daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:244:3: warning: product '65535 * 65535' of arguments 1 and 2 exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
> /daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:298:3: warning: argument 1 value '4294967294' exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
> /daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:299:3: warning: argument 1 value '4294967295' exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]

I assume this was on an ILP32 target like i686.  It should be fixed
by r243497 along with the failures on arm* targets that Christophe
pointed out earlier.  Please let me know if that doesn't clear it
up for you (and what target).

Thanks
Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-12-09 17:44         ` Martin Sebor
@ 2016-12-09 19:44           ` Jakub Jelinek
  0 siblings, 0 replies; 16+ messages in thread
From: Jakub Jelinek @ 2016-12-09 19:44 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Andreas Schwab, Gcc Patch List

On Fri, Dec 09, 2016 at 10:44:17AM -0700, Martin Sebor wrote:
> On 12/09/2016 06:26 AM, Andreas Schwab wrote:
> >FAIL: gcc.dg/attr-alloc_size-3.c  (test for warnings, line 445)
> >FAIL: gcc.dg/attr-alloc_size-3.c (test for excess errors)
> >Excess errors:
> >/daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:244:3: warning: product '65535 * 65535' of arguments 1 and 2 exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
> >/daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:298:3: warning: argument 1 value '4294967294' exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
> >/daten/aranym/gcc/gcc-20161209/gcc/testsuite/gcc.dg/attr-alloc_size-3.c:299:3: warning: argument 1 value '4294967295' exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]
> 
> I assume this was on an ILP32 target like i686.  It should be fixed
> by r243497 along with the failures on arm* targets that Christophe
> pointed out earlier.  Please let me know if that doesn't clear it
> up for you (and what target).

	on arm-unknown-linux-gnueabi (and likely other ILP32) targets.
doesn't look like valid ChangeLog entry (r243497).
Also, especially when adding new tests related to gimple-ssa-sprintf.c
that keep failing on non-x86_64 a lot, it would be nice if you at least
tested them through
make check-gcc RUNTESTFLAGS="--target_board=unix\{-m32,-m64\} dg.exp=name_of_test*"
first.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
  2016-11-20 11:56 Bernd Edlinger
@ 2016-11-20 23:48 ` Martin Sebor
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Sebor @ 2016-11-20 23:48 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gcc-patches

On 11/20/2016 04:56 AM, Bernd Edlinger wrote:
> Hi,
>
> when you add a returns_nonnull to the builtin alloca
> then this code in tree-vrp.c (gimple_stmt_nonzero_warnv_p)
> should go away:
>
>          if (flag_delete_null_pointer_checks &&
>              lookup_attribute ("returns_nonnull",
>                                TYPE_ATTRIBUTES (gimple_call_fntype (stmt))))
>            return true;
>          return gimple_alloca_call_p (stmt);

Yes, that looks like it should be superfluous now.  Let me test
it.  (It will make the null pointer test removal conditional on
flag_delete_null_pointer_checks but presumably that's okay.)

>
> Regarding __builtin_alloca_with_align, I see no test cases
> for that function:
>
> __builtin_alloca_with_align is a bit special, because it allocates
> more than the first parameter says, I think it allocates (if size != 0)
> size + align/8 in order to be able to return an aligned object.
> What if align is very large?

I'm guessing there are only a handful of tests for
__builtin_alloca_with_align because the built-in is used to allocate
VLAs and so it's exercised indirectly by VLA tests.  There is one
fairly comprehensive test for the built-in that exercises the case
you mention: builtins-68.c, though perhaps not to the extent it
could or should.  The problem is that there is no mechanism for
a compiler to indicate that a VLA has failed to allocate.  So while
the following compiles (with my patch with a warning), it crashes
at runtime:

$ cat x.c && gcc -O2 -S x.c
void sink (void*);

void g (void)
{
   struct S { int _Alignas (1024) i; };

   unsigned long N = __SIZE_MAX__ / _Alignof (struct S);

   struct S vla [N];
   sink (vla);
}


x.c: In function ‘g’:
x.c:9:12: warning: argument 1 value ‘18446744073709550592ul’ exceeds 
maximum object size 9223372036854775807 [-Walloc-size-larger-than=]
    struct S vla [N];
             ^~~
x.c:9:12: note: in a call to built-in allocation function 
‘__builtin_alloca_with_align’

Or, with -Wvla-larger-than=1000 (or any other positive number):

x.c:9:12: warning: argument to variable-length array is too large 
[-Wvla-larger-than=]
    struct S vla [N];
             ^~~
x.c:9:12: note: limit is 1000 bytes, but argument is 18446744073709550592

> I still would prefer separate -walloc-zero and -Walloca-zero
> options.  Likewise for the -Walloc-larger-than and -Walloca-larger-than
> or better -Wmalloc-larger-than and -Walloca-larger-than ?

Sure, I'm open to it.  But before I invest time into tweaking
things further I would like to get consensus on the two patches
I've posted (alloca(0) and this one), and ideally get them
at least tentatively approved.  Otherwise, I would worry it
might be a wasted effort.

Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284)
@ 2016-11-20 11:56 Bernd Edlinger
  2016-11-20 23:48 ` Martin Sebor
  0 siblings, 1 reply; 16+ messages in thread
From: Bernd Edlinger @ 2016-11-20 11:56 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches

Hi,

when you add a returns_nonnull to the builtin alloca
then this code in tree-vrp.c (gimple_stmt_nonzero_warnv_p)
should go away:

         if (flag_delete_null_pointer_checks &&
             lookup_attribute ("returns_nonnull",
                               TYPE_ATTRIBUTES (gimple_call_fntype (stmt))))
           return true;
         return gimple_alloca_call_p (stmt);


Regarding __builtin_alloca_with_align, I see no test cases
for that function:

__builtin_alloca_with_align is a bit special, because it allocates
more than the first parameter says, I think it allocates (if size != 0)
size + align/8 in order to be able to return an aligned object.
What if align is very large?

I still would prefer separate -walloc-zero and -Walloca-zero
options.  Likewise for the -Walloc-larger-than and -Walloca-larger-than
or better -Wmalloc-larger-than and -Walloca-larger-than ?


Bernd.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-12-09 19:44 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-14  3:19 [PATCH] warn on overflow in calls to allocation functions (bugs 77531 and 78284) Martin Sebor
2016-11-14 20:34 ` Eric Gallager
2016-11-16 17:06   ` Martin Sebor
2016-11-16 17:20 ` Martin Sebor
2016-11-18  0:15   ` Martin Sebor
2016-11-20  0:56     ` Martin Sebor
2016-12-02 23:28       ` Jeff Law
2016-12-07 22:04       ` Jeff Law
2016-12-08 23:55         ` Martin Sebor
2016-12-09  9:59           ` Christophe Lyon
2016-12-09 17:39             ` Martin Sebor
2016-12-09 13:26       ` Andreas Schwab
2016-12-09 17:44         ` Martin Sebor
2016-12-09 19:44           ` Jakub Jelinek
2016-11-20 11:56 Bernd Edlinger
2016-11-20 23:48 ` Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).