--- gcc/doc/invoke.texi.jj	2023-12-18 09:39:49.411355496 +0100
+++ gcc/doc/invoke.texi	2023-12-18 19:59:37.139525128 +0100
@@ -328,7 +328,7 @@ Objective-C and Objective-C++ Dialects}.
 -pedantic-errors -fpermissive
 -w  -Wextra  -Wall  -Wabi=@var{n}
 -Waddress  -Wno-address-of-packed-member  -Waggregate-return
--Walloc-size-larger-than=@var{byte-size}  -Walloc-zero
+-Walloc-size  -Walloc-size-larger-than=@var{byte-size}  -Walloc-zero
 -Walloca  -Walloca-larger-than=@var{byte-size}
 -Wno-aggressive-loop-optimizations
 -Warith-conversion
@@ -344,6 +344,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wc++20-compat
 -Wno-c++11-extensions  -Wno-c++14-extensions -Wno-c++17-extensions
 -Wno-c++20-extensions  -Wno-c++23-extensions
+-Wcalloc-transposed-args
 -Wcast-align  -Wcast-align=strict  -Wcast-function-type  -Wcast-qual
 -Wchar-subscripts
 -Wclobbered  -Wcomment
@@ -8260,8 +8261,7 @@ Warn about calls to allocation functions
 @code{alloc_size} that specify insufficient size for the target type of
 the pointer the result is assigned to, including those to the built-in
 forms of the functions @code{aligned_alloc}, @code{alloca},
-@code{calloc},
-@code{malloc}, and @code{realloc}.
+@code{calloc}, @code{malloc}, and @code{realloc}.
 
 @opindex Wno-alloc-zero
 @opindex Walloc-zero
@@ -8274,6 +8274,21 @@ when called with a zero size differs amo
 of @code{realloc} has been deprecated) relying on it may result in subtle
 portability bugs and should be avoided.
 
+@opindex Wcalloc-transposed-args
+@opindex Wno-calloc-transposed-args
+@item -Wcalloc-transposed-args
+Warn about calls to allocation functions decorated with attribute
+@code{alloc_size} with two arguments, which use @code{sizeof} operator
+as the earlier size argument and don't use it as the later size argument.
+This is a coding style warning.  The first argument to @code{calloc} is
+documented to be number of elements in array, while the second argument
+is size of each element, so @code{calloc (@var{n}, sizeof (int))} is preferred
+over @code{calloc (sizeof (int), @var{n})}.  If @code{sizeof} in the earlier
+argument and not the latter is intentional, the warning can be suppressed
+by using @code{calloc (sizeof (struct @var{S}) + 0, n)} or
+@code{calloc (1 * sizeof (struct @var{S}), 4)} or using @code{sizeof} in the
+later argument as well.
+
 @opindex Walloc-size-larger-than=
 @opindex Wno-alloc-size-larger-than
 @item -Walloc-size-larger-than=@var{byte-size}
--- gcc/c-family/c.opt.jj	2023-12-14 07:49:52.951583511 +0100
+++ gcc/c-family/c.opt	2023-12-18 13:57:11.834184689 +0100
@@ -502,6 +502,10 @@ Wc++26-extensions
 C++ ObjC++ Var(warn_cxx26_extensions) Warning Init(1)
 Warn about C++26 constructs in code compiled with an older standard.
 
+Wcalloc-transposed-args
+C ObjC C++ ObjC++ Var(warn_calloc_transposed_args) Warning LangEnabledBy(C ObjC C++ ObjC++,Wextra)
+Warn about suspicious calls to calloc-like functions where sizeof expression is the earlier size argument and not the latter.
+
 Wcast-function-type
 C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra)
 Warn about casts between incompatible function types.
--- gcc/c-family/c-common.h.jj	2023-12-14 07:49:52.915584002 +0100
+++ gcc/c-family/c-common.h	2023-12-18 16:26:40.420196735 +0100
@@ -1599,6 +1599,9 @@ extern void warn_about_parentheses (loca
 extern void warn_for_unused_label (tree label);
 extern void warn_for_div_by_zero (location_t, tree divisor);
 extern void warn_for_memset (location_t, tree, tree, int);
+extern void warn_for_calloc (location_t *, tree, vec<tree, va_gc> *,
+			     tree *, tree);
+extern void warn_for_alloc_size (location_t, tree, tree, tree);
 extern void warn_for_sign_compare (location_t,
 				   tree orig_op0, tree orig_op1,
 				   tree op0, tree op1,
--- gcc/c-family/c-warn.cc.jj	2023-12-14 07:49:52.951583511 +0100
+++ gcc/c-family/c-warn.cc	2023-12-18 19:30:47.285607029 +0100
@@ -2263,6 +2263,76 @@ warn_for_memset (location_t loc, tree ar
     }
 }
 
+/* Warn for calloc (sizeof (X), n).  */
+
+void
+warn_for_calloc (location_t *sizeof_arg_loc, tree callee,
+		 vec<tree, va_gc> *params, tree *sizeof_arg, tree attr)
+{
+  if (!TREE_VALUE (attr) || !TREE_CHAIN (TREE_VALUE (attr)))
+    return;
+
+  int arg1 = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr))) - 1;
+  int arg2
+    = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (TREE_VALUE (attr)))) - 1;
+  if (arg1 < 0
+      || (unsigned) arg1 >= vec_safe_length (params)
+      || arg1 >= 6
+      || arg2 < 0
+      || (unsigned) arg2 >= vec_safe_length (params)
+      || arg2 >= 6
+      || arg1 >= arg2)
+    return;
+
+  if (sizeof_arg[arg1] == NULL_TREE || sizeof_arg[arg2] != NULL_TREE)
+    return;
+
+  if (warning_at (sizeof_arg_loc[arg1], OPT_Wcalloc_transposed_args,
+		  "%qD sizes specified with %<sizeof%> in the earlier "
+		  "argument and not in the later argument", callee))
+    inform (sizeof_arg_loc[arg1], "earlier argument should specify number "
+	    "of elements, later size of each element");
+}
+
+/* Warn for allocator calls where the constant allocated size is smaller
+   than sizeof (TYPE).  */
+
+void
+warn_for_alloc_size (location_t loc, tree type, tree call, tree alloc_size)
+{
+  if (!TREE_VALUE (alloc_size))
+    return;
+
+  tree arg1 = TREE_VALUE (TREE_VALUE (alloc_size));
+  int idx1 = TREE_INT_CST_LOW (arg1) - 1;
+  if (idx1 < 0 || idx1 >= call_expr_nargs (call))
+    return;
+  arg1 = CALL_EXPR_ARG (call, idx1);
+  if (TREE_CODE (arg1) != INTEGER_CST)
+    return;
+  if (TREE_CHAIN (TREE_VALUE (alloc_size)))
+    {
+      tree arg2 = TREE_VALUE (TREE_CHAIN (TREE_VALUE (alloc_size)));
+      int idx2 = TREE_INT_CST_LOW (arg2) - 1;
+      if (idx2 < 0 || idx2 >= call_expr_nargs (call))
+	return;
+      arg2 = CALL_EXPR_ARG (call, idx2);
+      if (TREE_CODE (arg2) != INTEGER_CST)
+	return;
+      arg1 = int_const_binop (MULT_EXPR, fold_convert (sizetype, arg1),
+			      fold_convert (sizetype, arg2));
+      if (TREE_CODE (arg1) != INTEGER_CST)
+	return;
+    }
+  if (!VOID_TYPE_P (type)
+      && TYPE_SIZE_UNIT (type)
+      && TREE_CODE (TYPE_SIZE_UNIT (type)) == INTEGER_CST
+      && tree_int_cst_lt (arg1, TYPE_SIZE_UNIT (type)))
+    warning_at (loc, OPT_Walloc_size,
+		"allocation of insufficient size %qE for type %qT with "
+		"size %qE", arg1, type, TYPE_SIZE_UNIT (type));
+}
+
 /* Subroutine of build_binary_op. Give warnings for comparisons
    between signed and unsigned quantities that may fail. Do the
    checking based on the original operand trees ORIG_OP0 and ORIG_OP1,
--- gcc/c/c-parser.cc.jj	2023-12-14 07:49:52.969583266 +0100
+++ gcc/c/c-parser.cc	2023-12-18 19:24:01.508298779 +0100
@@ -12478,8 +12478,8 @@ c_parser_postfix_expression_after_primar
 {
   struct c_expr orig_expr;
   tree ident, idx;
-  location_t sizeof_arg_loc[3], comp_loc;
-  tree sizeof_arg[3];
+  location_t sizeof_arg_loc[6], comp_loc;
+  tree sizeof_arg[6];
   unsigned int literal_zero_mask;
   unsigned int i;
   vec<tree, va_gc> *exprlist;
@@ -12512,7 +12512,7 @@ c_parser_postfix_expression_after_primar
 	  {
 	    matching_parens parens;
 	    parens.consume_open (parser);
-	    for (i = 0; i < 3; i++)
+	    for (i = 0; i < 6; i++)
 	      {
 		sizeof_arg[i] = NULL_TREE;
 		sizeof_arg_loc[i] = UNKNOWN_LOCATION;
@@ -12577,6 +12577,13 @@ c_parser_postfix_expression_after_primar
 				      "not permitted in intervening code");
 		  parser->omp_for_parse_state->fail = true;
 		}
+	      if (warn_calloc_transposed_args)
+		if (tree attr = lookup_attribute ("alloc_size",
+						  TYPE_ATTRIBUTES
+						    (TREE_TYPE (expr.value))))
+		  if (TREE_VALUE (attr) && TREE_CHAIN (TREE_VALUE (attr)))
+		    warn_for_calloc (sizeof_arg_loc, expr.value, exprlist,
+				     sizeof_arg, attr);
 	    }
 
 	  start = expr.get_start ();
@@ -12861,7 +12868,7 @@ c_parser_expr_list (c_parser *parser, bo
 	vec_safe_push (orig_types, expr.original_type);
       if (locations)
 	locations->safe_push (expr.get_location ());
-      if (++idx < 3
+      if (++idx < 6
 	  && sizeof_arg != NULL
 	  && (expr.original_code == SIZEOF_EXPR
 	      || expr.original_code == PAREN_SIZEOF_EXPR))
--- gcc/c/c-typeck.cc.jj	2023-12-14 07:49:52.990582980 +0100
+++ gcc/c/c-typeck.cc	2023-12-18 19:01:40.013746860 +0100
@@ -6054,6 +6054,19 @@ build_c_cast (location_t loc, tree type,
 			    c_addr_space_name (as_to),
 			    c_addr_space_name (as_from));
 	    }
+
+	  /* Warn of new allocations that are not big enough for the target
+	     type.  */
+	  if (warn_alloc_size && TREE_CODE (value) == CALL_EXPR)
+	    if (tree fndecl = get_callee_fndecl (value))
+	      if (DECL_IS_MALLOC (fndecl))
+		{
+		  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
+		  tree alloc_size = lookup_attribute ("alloc_size", attrs);
+		  if (alloc_size)
+		    warn_for_alloc_size (loc, TREE_TYPE (type), value,
+					 alloc_size);
+		}
 	}
 
       /* Warn about possible alignment problems.  */
@@ -7277,32 +7290,15 @@ convert_for_assignment (location_t locat
 
       /* Warn of new allocations that are not big enough for the target
 	 type.  */
-      tree fndecl;
-      if (warn_alloc_size
-	  && TREE_CODE (rhs) == CALL_EXPR
-	  && (fndecl = get_callee_fndecl (rhs)) != NULL_TREE
-	  && DECL_IS_MALLOC (fndecl))
-	{
-	  tree fntype = TREE_TYPE (fndecl);
-	  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
-	  tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs);
-	  if (alloc_size)
+      if (warn_alloc_size && TREE_CODE (rhs) == CALL_EXPR)
+	if (tree fndecl = get_callee_fndecl (rhs))
+	  if (DECL_IS_MALLOC (fndecl))
 	    {
-	      tree args = TREE_VALUE (alloc_size);
-	      int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
-	      /* For calloc only use the second argument.  */
-	      if (TREE_CHAIN (args))
-		idx = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
-	      tree arg = CALL_EXPR_ARG (rhs, idx);
-	      if (TREE_CODE (arg) == INTEGER_CST
-		  && !VOID_TYPE_P (ttl) && TYPE_SIZE_UNIT (ttl)
-		  && INTEGER_CST == TREE_CODE (TYPE_SIZE_UNIT (ttl))
-		  && tree_int_cst_lt (arg, TYPE_SIZE_UNIT (ttl)))
-		 warning_at (location, OPT_Walloc_size, "allocation of "
-			     "insufficient size %qE for type %qT with "
-			     "size %qE", arg, ttl, TYPE_SIZE_UNIT (ttl));
+	      tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
+	      tree alloc_size = lookup_attribute ("alloc_size", attrs);
+	      if (alloc_size)
+		warn_for_alloc_size (location, ttl, rhs, alloc_size);
 	    }
-	}
 
       /* See if the pointers point to incompatible address spaces.  */
       asl = TYPE_ADDR_SPACE (ttl);
--- gcc/testsuite/gcc.dg/Walloc-size-4.c.jj	2023-12-18 19:06:41.069528176 +0100
+++ gcc/testsuite/gcc.dg/Walloc-size-4.c	2023-12-18 19:34:06.964824180 +0100
@@ -0,0 +1,54 @@
+/* Tests the warnings for insufficient allocation size.  */
+/* { dg-do compile } */
+/* { dg-options "-Walloc-size -Wno-calloc-transposed-args" } */
+
+struct S { int x[10]; };
+void bar (struct S *);
+typedef __SIZE_TYPE__ size_t;
+void *myfree (void *, int, int);
+void *mymalloc (int, int, size_t) __attribute__((malloc, malloc (myfree), alloc_size (3)));
+void *mycalloc (int, int, size_t, size_t) __attribute__((malloc, malloc (myfree), alloc_size (3, 4)));
+
+void
+foo (void)
+{
+  struct S *p = (struct S *) __builtin_malloc (sizeof *p);
+  __builtin_free (p);
+  p = (struct S *) __builtin_malloc (sizeof p);		/* { dg-warning "allocation of insufficient size" } */
+  __builtin_free (p);
+  p = (struct S *) __builtin_alloca (sizeof p);		/* { dg-warning "allocation of insufficient size" } */
+  bar (p);
+  p = (struct S *) __builtin_calloc (1, sizeof p);	/* { dg-warning "allocation of insufficient size" } */
+  __builtin_free (p);
+  bar ((struct S *) __builtin_malloc (4));		/* { dg-warning "allocation of insufficient size" } */
+  __builtin_free (p);
+  p = (struct S *) __builtin_calloc (sizeof *p, 1);
+  __builtin_free (p);
+  p = __builtin_calloc (sizeof *p, 1);
+  __builtin_free (p);
+}
+
+void
+baz (void)
+{
+  struct S *p = (struct S *) mymalloc (42, 42, sizeof *p);
+  myfree (p, 42, 42);
+  p = (struct S *) mymalloc (42, 42, sizeof p);		/* { dg-warning "allocation of insufficient size" } */
+  myfree (p, 42, 42);
+  p = (struct S *) mycalloc (42, 42, 1, sizeof p);	/* { dg-warning "allocation of insufficient size" } */
+  myfree (p, 42, 42);
+  bar ((struct S *) mymalloc (42, 42, 4));		/* { dg-warning "allocation of insufficient size" } */
+  myfree (p, 42, 42);
+  p = (struct S *) mycalloc (42, 42, sizeof *p, 1);
+  myfree (p, 42, 42);
+  p = mycalloc (42, 42, sizeof *p, 1);
+  myfree (p, 42, 42);
+  p = mymalloc (42, 42, sizeof *p);
+  myfree (p, 42, 42);
+  p = mymalloc (42, 42, sizeof p);			/* { dg-warning "allocation of insufficient size" } */
+  myfree (p, 42, 42);
+  p = mycalloc (42, 42, 1, sizeof p);			/* { dg-warning "allocation of insufficient size" } */
+  myfree (p, 42, 42);
+  bar (mymalloc (42, 42, 4));				/* { dg-warning "allocation of insufficient size" } */
+  myfree (p, 42, 42);
+}
--- gcc/testsuite/gcc.dg/Walloc-size-5.c.jj	2023-12-18 19:18:13.451180896 +0100
+++ gcc/testsuite/gcc.dg/Walloc-size-5.c	2023-12-18 19:19:25.244173868 +0100
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloc-size -std=gnu11" } */
+
+struct S { int x[10]; };
+void myfree ();
+void *mymalloc () __attribute__((malloc, alloc_size (16)));
+void *mycalloc () __attribute__((malloc, alloc_size (16, 17)));
+
+void
+foo (void)
+{
+  struct S *p = mymalloc (1);
+  myfree (p);
+  p = mycalloc (1, 1);
+  myfree (p);
+  p = (struct S *) mymalloc (1);
+  myfree (p);
+  p = (struct S *) mycalloc (1, 1);
+  myfree (p);
+}
--- gcc/testsuite/gcc.dg/Wcalloc-transposed-args-1.c.jj	2023-12-18 19:39:42.930196797 +0100
+++ gcc/testsuite/gcc.dg/Wcalloc-transposed-args-1.c	2023-12-18 19:52:03.273855456 +0100
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-options "-Wcalloc-transposed-args" } */
+
+typedef __SIZE_TYPE__ size_t;
+void free (void *);
+void *calloc (size_t, size_t);
+void *myfree (void *, int, int);
+void *mycalloc (int, int, size_t, size_t) __attribute__((malloc, malloc (myfree), alloc_size (3, 4)));
+
+void
+foo (int n)
+{
+  void *p;
+  p = __builtin_calloc (1, sizeof (int));
+  __builtin_free (p);
+  p = __builtin_calloc (n, sizeof (int));
+  __builtin_free (p);
+  p = __builtin_calloc (sizeof (int), 1);		/* { dg-warning "'__builtin_calloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  __builtin_free (p);					/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = __builtin_calloc (sizeof (int), n);		/* { dg-warning "'__builtin_calloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  __builtin_free (p);					/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = __builtin_calloc ((sizeof (int)), 1);		/* { dg-warning "'__builtin_calloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  __builtin_free (p);					/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = __builtin_calloc (sizeof (int) + 0, 1);
+  __builtin_free (p);
+  p = __builtin_calloc (sizeof (int), sizeof (char));
+  __builtin_free (p);
+  p = __builtin_calloc (1 * sizeof (int), 1);
+  __builtin_free (p);
+  p = calloc (1, sizeof (int));
+  free (p);
+  p = calloc (n, sizeof (int));
+  free (p);
+  p = calloc (sizeof (int), 1);				/* { dg-warning "'calloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  free (p);						/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = calloc (sizeof (int), n);				/* { dg-warning "'calloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  free (p);						/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = calloc (sizeof (int), sizeof (char));
+  free (p);
+  p = calloc (1 * sizeof (int), 1);
+  free (p);
+  p = mycalloc (42, 42, 1, sizeof (int));
+  myfree (p, 42, 42);
+  p = mycalloc (42, 42, n, sizeof (int));
+  myfree (p, 42, 42);
+  p = mycalloc (42, 42, sizeof (int), 1);		/* { dg-warning "'mycalloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  myfree (p, 42, 42);					/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = mycalloc (42, 42, sizeof (int), n);		/* { dg-warning "'mycalloc' sizes specified with 'sizeof' in the earlier argument and not in the later argument" } */
+  myfree (p, 42, 42);					/* { dg-message "earlier argument should specify number of elements, later size of each element" "" { target *-*-* } .-1 } */
+  p = mycalloc (42, 42, sizeof (int), sizeof (char));
+  myfree (p, 42, 42);
+  p = mycalloc (42, 42, 1 * sizeof (int), 1);
+  myfree (p, 42, 42);
+}
