public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
@ 2015-10-19 23:06 Martin Sebor
  2015-10-27  2:47 ` [PING] " Martin Sebor
  0 siblings, 1 reply; 9+ messages in thread
From: Martin Sebor @ 2015-10-19 23:06 UTC (permalink / raw)
  To: Gcc Patch List

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

This is a patch for two C++ bugs:

   67913 - new expression with negative size not diagnosed
   67927 - array new expression with excessive number of elements
           not diagnosed

The C++ front end rejects a subset of array declarators with negative
bounds or with bounds in excess of some implementation defined maximum
(roughly SIZE_MAX / 2), but it does so inconsistently.  For example,
it silently accepts expressions such as

     new int [-1][2];

but invokes operator new to allocate SIZE_MAX bytes.  When operator
new succeeds (as is the case on Linux with memory overcommitment
enabled), the new expression returns a valid pointer to some
unknown amount of storage less than SIZE_MAX.  Accessing the memory
at some non-zero offset then causes a SIGSEGV.

Similarly, GCC accepts the following expression with the same result:

     new int [SIZE_MAX][2];

C++ 14 makes it clear that such expressions are ill-formed and must
be rejected.  This patch adds checks that consistently reject all
new expressions with both negative array bounds and bounds in excess
of the maximum.

While I raised these bugs as separate issues I decided to group the
two sets of changes together since they both touch the same function
in similar ways, and hopefully doing so will make them also easier
to review.

I've tested the patch by bootstrapping C/C++ and running the test
suites (including libstdc++) on x86_64 with no regressions.

During the development of the changes I found a few basic mistakes
in my code only after running libstdc++ tests.  To make it possible
to uncover them sooner in the future, I added another test that
isn't directly related to the problem: new45.C.

I also found a minor problem in the GCC regression test suite where
the g++.dg/other/new-size-type.C test for PR 36741 tried to check
that the 'new char[~static_cast<size_t>(0)]' expression was accepted
without a warning.  The complaint in the PR was about the wording of
the warning, not about the validity of the expression (the submitter
agreed that a correctly worded diagnostic would be appropriate).
  I chaged the test to expect a meaningful error message.

Once this patch is approved and committed a follow-up patch should
document the implementation-defined maximum to the manual.

Martin

[-- Attachment #2: gcc-67913-67917-array-new-wrong-number-of-elements.patch --]
[-- Type: text/x-patch, Size: 44887 bytes --]

gcc/cp/ChangeLog

2015-10-19  Martin Sebor  <msebor@redhat.com>

	PR c++/67913
	PR c++/67927
	* call.c (build_operator_new_call): Do not assume size_check
	is non-null, analogously to the top half of the function.
	* init.c (build_new_1): Detect and diagnose array sizes in
	excess of the maximum of roughly SIZE_MAX / 2.
	Insert a runtime check only for arrays with a non-constant size.
	(build_new): Detect and diagnose negative array sizes.

gcc/testsuite/ChangeLog

2015-10-19  Martin Sebor  <msebor@redhat.com>

	* init/new45.C: New test to verify that operator new is invoked
	with or without overhead for a cookie.

	PR c++/67927
	* init/new44.C: New test for placement new expressions for arrays
	with excessive number of elements.

	PR c++/67913
	* init/new43.C: New test for placement new expressions for arrays
	with negative number of elements.

	* other/new-size-type.C: Expect array new expression with
	an excessive number of elements to be rejected.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 367d42b..3f76198 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4228,10 +4228,12 @@ build_operator_new_call (tree fnname, vec<tree, va_gc> **args,
 	 {
 	   /* Update the total size.  */
 	   *size = size_binop (PLUS_EXPR, original_size, *cookie_size);
+	   if (size_check)
+	     {
 	       /* Set to (size_t)-1 if the size check fails.  */
-	   gcc_assert (size_check != NULL_TREE);
 	       *size = fold_build3 (COND_EXPR, sizetype, size_check,
 				    *size, TYPE_MAX_VALUE (sizetype));
+	     }
 	   /* Update the argument list to reflect the adjusted size.  */
 	   (**args)[0] = *size;
 	 }
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..3db512d 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2272,7 +2272,11 @@ throw_bad_array_new_length (void)
 /* Generate code for a new-expression, including calling the "operator
    new" function, initializing the object, and, if an exception occurs
    during construction, cleaning up.  The arguments are as for
-   build_raw_new_expr.  This may change PLACEMENT and INIT.  */
+   build_raw_new_expr.  This may change PLACEMENT and INIT.
+   TYPE is the type of the object being constructed, possibly an array
+   of NELTS elements when NELTS is non-null (in "new T[NELTS]", T may
+   be an array of the form U[inner], with the whole expression being
+   "new U[NELTS][inner]").  */

 static tree
 build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
@@ -2292,13 +2296,16 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
      type.)  */
   tree pointer_type;
   tree non_const_pointer_type;
+  /* The most significant array bound in int[OUTER_NELTS][inner].  */
   tree outer_nelts = NULL_TREE;
-  /* For arrays, a bounds checks on the NELTS parameter. */
+  /* For arrays with a non-constant number of elements, a bounds checks
+     on the NELTS parameter to avoid integer overflow at runtime. */
   tree outer_nelts_check = NULL_TREE;
   bool outer_nelts_from_type = false;
+  /* Number of the "inner" elements in "new T[OUTER_NELTS][inner]".  */
   offset_int inner_nelts_count = 1;
   tree alloc_call, alloc_expr;
-  /* Size of the inner array elements. */
+  /* Size of the inner array elements (those with constant dimensions). */
   offset_int inner_size;
   /* The address returned by the call to "operator new".  This node is
      a VAR_DECL and is therefore reusable.  */
@@ -2492,21 +2499,41 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 	}

       max_outer_nelts = wi::udiv_trunc (max_size, inner_size);
-      /* Only keep the top-most seven bits, to simplify encoding the
-	 constant in the instruction stream.  */
+      max_outer_nelts_tree = wide_int_to_tree (sizetype, max_outer_nelts);
+
+      size = size_binop (MULT_EXPR, size, convert (sizetype, nelts));
+
+      if (TREE_CONSTANT (maybe_constant_value (outer_nelts)))
+	{
+	  if (tree_int_cst_lt (max_outer_nelts_tree, outer_nelts))
+	    {
+	      /* When the array size is constant, check it at compile time
+		 to make sure it doesn't exceed the implementation-defined
+		 maximum, as required by C++ 14 (in C++ 11 this requirement
+		 isn't explicitly stated but it's enforced anyway -- see
+		 grokdeclarator in cp/decl.c).  */
+	      if (complain & tf_error)
+		error ("size of array is too large");
+	      return error_mark_node;
+	    }
+	}
+      else
 	{
+	  /* When a runtime check is necessary because the array size
+	     isn't constant, keep only the top-most seven bits (starting
+	     with the most significant non-zero bit) of the maximum size
+	     to compare the array size against, to simplify encoding the
+	     constant maximum size in the instruction stream.  */
 	  unsigned shift = (max_outer_nelts.get_precision ()) - 7
 	    - wi::clz (max_outer_nelts);
 	  max_outer_nelts = wi::lshift (wi::lrshift (max_outer_nelts, shift),
 					shift);
-      }
-      max_outer_nelts_tree = wide_int_to_tree (sizetype, max_outer_nelts);

-      size = size_binop (MULT_EXPR, size, convert (sizetype, nelts));
 	  outer_nelts_check = fold_build2 (LE_EXPR, boolean_type_node,
 					   outer_nelts,
 					   max_outer_nelts_tree);
 	}
+    }

   alloc_fn = NULL_TREE;

@@ -3066,6 +3093,24 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
           else
             return error_mark_node;
         }
+
+      tree nelts_save = nelts;
+      nelts = maybe_constant_value (nelts);
+
+      if (!TREE_CONSTANT (nelts))
+	nelts = nelts_save;
+
+      /* The expression in a noptr-new-declarator is erroneous if it's of
+	 non-class type and its value before converting to std::size_t is
+	 less than zero. ... If the expression is a constant expression,
+	 the program is ill-fomed.  */
+      if (TREE_CONSTANT (nelts) && tree_int_cst_sgn (nelts) == -1)
+	{
+	  if (complain & tf_error)
+	    error ("size of array is negative");
+	  return error_mark_node;
+	}
+
       nelts = mark_rvalue_use (nelts);
       nelts = cp_save_expr (cp_convert (sizetype, nelts, complain));
     }
diff --git a/gcc/testsuite/g++.dg/init/new43.C b/gcc/testsuite/g++.dg/init/new43.C
new file mode 100644
index 0000000..9b08667
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new43.C
@@ -0,0 +1,164 @@
+/* { dg-do compile } */
+
+// Test for PR c++/67913 - new expression with negative size not diagnosed.
+typedef __typeof__ (sizeof 0) size_t;
+
+void* operator new (size_t, void*);
+void* operator new[] (size_t, void*);
+
+struct A {
+    int a [4];
+};
+
+struct B {
+    int a [4];
+
+    void* operator new (size_t, void*);
+    void* operator new[] (size_t, void*);
+};
+
+void* operator new (size_t, B*);
+void* operator new[] (size_t, B*);
+
+void *p;
+
+void test_literal ()
+{
+    char c;
+    (void)c;
+
+    B b;
+
+    // Verify integer literal.
+    p = new char [-1];           // { dg-error "size of array is negative" }
+    p = new char [2][-3];        // { dg-error "size of array is negative" }
+    p = new char [-4][5];        // { dg-error "size of array is negative" }
+    p = new char [-6][-7];       // { dg-error "size of array is negative" }
+
+    p = new (p) char [-1];       // { dg-error "size of array is negative" }
+    p = new (p) char [2][-3];    // { dg-error "size of array is negative" }
+    p = new (p) char [-4][5];    // { dg-error "size of array is negative" }
+    p = new (p) char [-6][-7];   // { dg-error "size of array is negative" }
+
+    p = new (p) A [-1];          // { dg-error "size of array is negative" }
+    p = new (p) A [2][-3];       // { dg-error "size of array is negative" }
+    p = new (p) A [-4][5];       // { dg-error "size of array is negative" }
+    p = new (p) A [-6][-7];      // { dg-error "size of array is negative" }
+
+    p = new (p) B [-1];          // { dg-error "size of array is negative" }
+    p = new (p) B [2][-3];       // { dg-error "size of array is negative" }
+    p = new (p) B [-4][5];       // { dg-error "size of array is negative" }
+    p = new (p) B [-6][-7];      // { dg-error "size of array is negative" }
+
+    p = new (&b) B [-1];          // { dg-error "size of array is negative" }
+    p = new (&b) B [2][-3];       // { dg-error "size of array is negative" }
+    p = new (&b) B [-4][5];       // { dg-error "size of array is negative" }
+    p = new (&b) B [-6][-7];      // { dg-error "size of array is negative" }
+
+    p = new char [1 - 2];         // { dg-error "size of array is negative" }
+    p = new (p) char [2 - 3];     // { dg-error "size of array is negative" }
+    p = new A [2 < 1 ? -1 : -2];  // { dg-error "size of array is negative" }
+    p = new (p) B [2 - 3 * 2];    // { dg-error "size of array is negative" }
+    p = new (&b) B [1][2 - 3 * 2];// { dg-error "size of array is negative" }
+}
+
+void test_constant_expression ()
+{
+    char c;
+    (void)c;
+
+    B b;
+
+    static const signed char i1 = -1;
+    static const signed short i2 = -2;
+    static const signed int i3 = -3;
+    static const signed long i4 = -4;
+    static const signed long long i5 = -5;
+    static const int i6 = -6;
+    static const int i7 = -7;
+
+    // Verify constant expression.
+    p = new char [i1];           // { dg-error "size of array is negative" }
+    p = new char [2][i3];        // { dg-error "size of array is negative" }
+    p = new char [i4][5];        // { dg-error "size of array is negative" }
+    p = new char [i6][i7];       // { dg-error "size of array is negative" }
+
+    p = new (p) char [i1];       // { dg-error "size of array is negative" }
+    p = new (p) char [2][i3];    // { dg-error "size of array is negative" }
+    p = new (p) char [i4][5];    // { dg-error "size of array is negative" }
+    p = new (p) char [i6][i7];   // { dg-error "size of array is negative" }
+
+    p = new (p) A [i1];          // { dg-error "size of array is negative" }
+    p = new (p) A [2][i3];       // { dg-error "size of array is negative" }
+    p = new (p) A [i4][5];       // { dg-error "size of array is negative" }
+    p = new (p) A [i6][i7];      // { dg-error "size of array is negative" }
+
+    p = new (p) B [i1];          // { dg-error "size of array is negative" }
+    p = new (p) B [2][i3];       // { dg-error "size of array is negative" }
+    p = new (p) B [i4][5];       // { dg-error "size of array is negative" }
+    p = new (p) B [i6][i7];      // { dg-error "size of array is negative" }
+
+    p = new (&b) B [i1];          // { dg-error "size of array is negative" }
+    p = new (&b) B [2][i3];       // { dg-error "size of array is negative" }
+    p = new (&b) B [i4][5];       // { dg-error "size of array is negative" }
+    p = new (&b) B [i6][i7];      // { dg-error "size of array is negative" }
+
+    p = new short [i1 - 2];       // { dg-error "size of array is negative" }
+    p = new (p) bool [i2 - 3];    // { dg-error "size of array is negative" }
+    p = new A [2 < 1 ? i1 : i2];  // { dg-error "size of array is negative" }
+    p = new (p) B [2 + i3 * 2];   // { dg-error "size of array is negative" }
+    p = new (&b) B [1][i1 - 3 * 2];// { dg-error "size of array is negative" }
+}
+
+void test_constexpr ()
+{
+    B b;
+
+#if __cplusplus >= 201103L
+
+    // Verify that a constant expression that is "a prvalue core constant
+    // expression whose value is an object where, for that object and its
+    // subobjects each non-static data member of reference type refers to
+    // an object with static storage duration."
+    static constexpr struct S {
+        int i_;
+        constexpr S (int i): i_ (i) { }
+        constexpr operator int () const { return i_; }
+    } s1 (-1), s2 (-2), s3 (-3), s4 (-4), s5 (-5), s6 (-6), s7 (-7);
+#else
+    // C++ 11 constexpr is not available, fall back on plain ole enum.
+    enum { s1 = -1, s2 = -2, s3 = -3, s4 = -4, s5 = -5, s6 = -6, s7 = -7 };
+#endif
+
+    // Verify constant expression.
+    p = new char [s1];           // { dg-error "size of array is negative" }
+    p = new char [2][s3];        // { dg-error "size of array is negative" }
+    p = new char [s4][5];        // { dg-error "size of array is negative" }
+    p = new char [s6][s7];       // { dg-error "size of array is negative" }
+
+    p = new (p) char [s1];       // { dg-error "size of array is negative" }
+    p = new (p) char [2][s3];    // { dg-error "size of array is negative" }
+    p = new (p) char [s4][5];    // { dg-error "size of array is negative" }
+    p = new (p) char [s6][s7];   // { dg-error "size of array is negative" }
+
+    p = new (p) A [s1];          // { dg-error "size of array is negative" }
+    p = new (p) A [2][s3];       // { dg-error "size of array is negative" }
+    p = new (p) A [s4][5];       // { dg-error "size of array is negative" }
+    p = new (p) A [s6][s7];      // { dg-error "size of array is negative" }
+
+    p = new (p) B [s1];          // { dg-error "size of array is negative" }
+    p = new (p) B [2][s3];       // { dg-error "size of array is negative" }
+    p = new (p) B [s4][5];       // { dg-error "size of array is negative" }
+    p = new (p) B [s6][s7];      // { dg-error "size of array is negative" }
+
+    p = new (&b) B [s1];          // { dg-error "size of array is negative" }
+    p = new (&b) B [2][s3];       // { dg-error "size of array is negative" }
+    p = new (&b) B [s4][5];       // { dg-error "size of array is negative" }
+    p = new (&b) B [s6][s7];      // { dg-error "size of array is negative" }
+
+    p = new int [s1 + s2];           // { dg-error "size of array is negative" }
+    p = new (p) long [2 * s3];       // { dg-error "size of array is negative" }
+    p = new A [s2 < s1 ? s1 : s2];   // { dg-error "size of array is negative" }
+    p = new (p) B [s7 - s2 * 2];     // { dg-error "size of array is negative" }
+    p = new (&b) B [9][s4 - s1 * 2]; // { dg-error "size of array is negative" }
+}
diff --git a/gcc/testsuite/g++.dg/init/new44.C b/gcc/testsuite/g++.dg/init/new44.C
new file mode 100644
index 0000000..d6ff86a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new44.C
@@ -0,0 +1,532 @@
+// { dg-do compile }
+
+// Test for PR c++/67927 - array new expression with excessive number
+// of elements not diagnosed.
+
+// GCC uses a different maximum value at compile time and at runtime:
+// 1) The compile-time maximum, MAX, is SIZE_MAX / 2 minus the size
+//    of a cookie (sizeof (size_t)).  Exceeding the compile-time
+//    maximum is ill-formed and diagnosed.  This test verifies this
+//    diagnostic.
+// 2) The runtime runtime maximum is the most significant 7 bits,
+//    starting with the first most significant non-zero bit, of
+//    the dividend of the compile-time constant MAX and the product
+//    of the constant array dimensions and the element size, minus
+//    the size of the "cookie."  This is also roughly (though not
+//    exactly) SIZE_MAX / 2.  Exceeding the runtime maximum is
+//    diagnosed at runtime by throwing a bad_array_new_length
+//    exception.
+//    The cookie is the number of elements in the array, and is only
+//    added for non-POD types or user-defined overloads of placement
+//    new, but the its size factors into the maximum size formula
+//    regardless.
+
+// See also PR c++/19351 - integer overflow in operator new[].
+
+// For convenience.
+#define MAX __SIZE_MAX__
+
+typedef __typeof__ (sizeof 0) size_t;
+
+void* operator new (size_t, void*);
+void* operator new[] (size_t, void*);
+
+void *p;
+
+// Exercise new expression with one-dimensional arrays of char.
+static void __attribute__ ((used))
+test_one_dim_char_array ()
+{
+    p = new char [MAX];                 // { dg-error "size of array" }
+    p = new char [MAX - 1];             // { dg-error "size of array" }
+    p = new char [MAX - 2];             // { dg-error "size of array" }
+    p = new char [MAX - 99];            // { dg-error "size of array" }
+    p = new char [MAX / 2];             // { dg-error "size of array" }
+    p = new char [MAX / 2 - 1];         // { dg-error "size of array" }
+    p = new char [MAX / 2 - 2];         // { dg-error "size of array" }
+
+    // Avoid testing the expressions below since whether or not they
+    // are accepted depends on the precision of size_t (which also
+    // determines the size of the cookie).
+    // p = new char [MAX / 2 - 3];
+    // p = new char [MAX / 2 - 4];
+    // p = new char [MAX / 2 - 5];
+    // p = new char [MAX / 2 - 6];
+
+    // The following expressions are accepted on ILP32 as well LP64
+    // (they will be diagnosed on LP128 if there ever is such a data
+    // model).
+    p = new char [MAX / 2 - 7];         // okay
+    p = new char [MAX / 2 - 8];         // okay
+}
+
+static void __attribute__ ((used))
+test_one_dim_short_array ()
+{
+    p = new short [MAX];                // { dg-error "size of array" }
+    p = new short [MAX - 1];            // { dg-error "size of array" }
+    p = new short [MAX - 2];            // { dg-error "size of array" }
+    p = new short [MAX - 99];           // { dg-error "size of array" }
+    p = new short [MAX / 2];            // { dg-error "size of array" }
+    p = new short [MAX / 2 - 1];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 2];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 3];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 4];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 5];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 6];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 7];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 8];        // { dg-error "size of array" }
+    p = new short [MAX / 4];            // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new short [MAX / 4 - 1];
+
+    p = new short [MAX / 4 - 4];        // okay
+}
+
+// Exercise new expression with two-dimensional arrays or char.
+static void __attribute__ ((used))
+test_two_dim_char_array ()
+{
+    p = new char [1][MAX];              // { dg-error "size of array" }
+    p = new char [1][MAX - 1];          // { dg-error "size of array" }
+    p = new char [1][MAX - 2];          // { dg-error "size of array" }
+    p = new char [1][MAX - 99];         // { dg-error "size of array" }
+    p = new char [1][MAX / 2];          // { dg-error "size of array" }
+    p = new char [1][MAX / 2 - 1];      // { dg-error "size of array" }
+    p = new char [1][MAX / 2 - 2];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [1][MAX / 2 - 3];
+    // p = new char [1][MAX / 2 - 4];
+    // p = new char [1][MAX / 2 - 5];
+    // p = new char [1][MAX / 2 - 6];
+
+    p = new char [1][MAX / 2 - 7];      // okay
+    p = new char [1][MAX / 2 - 8];      // okay
+
+    p = new char [2][MAX];              // { dg-error "size of array" }
+    p = new char [2][MAX - 1];          // { dg-error "size of array" }
+    p = new char [2][MAX - 2];          // { dg-error "size of array" }
+    p = new char [2][MAX / 2];          // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 1];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 2];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 7];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 8];      // { dg-error "size of array" }
+
+    p = new char [MAX][MAX];            // { dg-error "size of array" }
+    p = new char [MAX][MAX - 1];        // { dg-error "size of array" }
+    p = new char [MAX][MAX - 2];        // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2];        // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 1];    // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 2];    // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 7];    // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 8];    // { dg-error "size of array" }
+    p = new char [MAX][2];              // { dg-error "size of array" }
+    p = new char [MAX][1];              // { dg-error "size of array" }
+    p = new char [MAX / 2][1];          // { dg-error "size of array" }
+    p = new char [MAX / 2 - 1][1];      // { dg-error "size of array" }
+    p = new char [MAX / 2 - 2][1];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [MAX / 2 - 3][1];
+    // p = new char [MAX / 2 - 4][1];
+    // p = new char [MAX / 2 - 5][1];
+    // p = new char [MAX / 2 - 6][1];
+
+    p = new char [MAX / 2 - 7][1];      // okay
+    p = new char [MAX / 2 - 8][1];      // okay
+}
+
+
+// Exercise new expression with three-dimensional arrays.
+static __attribute__ ((used)) void
+test_three_dim_char_array ()
+{
+    p = new char [1][1][MAX];           // { dg-error "size of array" }
+    p = new char [1][1][MAX - 1];       // { dg-error "size of array" }
+    p = new char [1][1][MAX - 2];       // { dg-error "size of array" }
+    p = new char [1][1][MAX - 99];      // { dg-error "size of array" }
+    p = new char [1][1][MAX / 2];       // { dg-error "size of array" }
+    p = new char [1][1][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [1][1][MAX / 2 - 2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [1][1][MAX / 2 - 3];
+    // p = new char [1][1][MAX / 2 - 4];
+    // p = new char [1][1][MAX / 2 - 5];
+    // p = new char [1][1][MAX / 2 - 6];
+
+    p = new char [1][1][MAX / 2 - 7];   // okay
+    p = new char [1][1][MAX / 2 - 8];   // okay
+
+    p = new char [1][2][MAX];           // { dg-error "size of array" }
+    p = new char [1][2][MAX - 1];       // { dg-error "size of array" }
+    p = new char [1][2][MAX - 2];       // { dg-error "size of array" }
+    p = new char [1][2][MAX - 99];      // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2];       // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 3];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 4];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 5];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 6];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 4];       // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [1][2][MAX / 4 - 1];
+    // p = new char [1][2][MAX / 4 - 2];
+
+    p = new char [1][2][MAX / 4 - 3];   // okay
+    p = new char [1][2][MAX / 4 - 4];   // okay
+
+    p = new char [2][1][MAX];           // { dg-error "size of array" }
+    p = new char [2][1][MAX - 1];       // { dg-error "size of array" }
+    p = new char [2][1][MAX - 2];       // { dg-error "size of array" }
+    p = new char [2][1][MAX - 99];      // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2];       // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 3];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 4];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 5];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 6];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 4];       // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [2][1][MAX / 4 - 1];
+    // p = new char [2][1][MAX / 4 - 2];
+
+    p = new char [2][1][MAX / 4 - 3];   // okay
+    p = new char [2][1][MAX / 4 - 4];   // okay
+
+    p = new char [2][2][MAX];           // { dg-error "size of array" }
+    p = new char [2][2][MAX - 1];       // { dg-error "size of array" }
+    p = new char [2][2][MAX - 2];       // { dg-error "size of array" }
+    p = new char [2][2][MAX - 99];      // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2];       // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 3];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 4];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 5];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 6];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 4];       // { dg-error "size of array" }
+    p = new char [2][2][MAX / 4 - 1];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 4 - 2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [2][2][MAX / 8];
+    // p = new char [2][2][MAX / 8 - 1];
+
+    p = new char [2][2][MAX / 8 - 2];
+    p = new char [2][2][MAX / 8 - 3];
+
+    p = new char [2][MAX][2];           // { dg-error "size of array" }
+    p = new char [2][MAX - 1][2];       // { dg-error "size of array" }
+    p = new char [2][MAX - 2][2];       // { dg-error "size of array" }
+    p = new char [2][MAX - 99][2];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2][2];       // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 1][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 2][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 3][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 4][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 5][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 6][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 7][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 8][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 4][2];       // { dg-error "size of array" }
+    p = new char [2][MAX / 4 - 1][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 4 - 2][2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [2][MAX / 8][2];
+    // p = new char [2][MAX / 8 - 1][2];
+
+    p = new char [2][MAX / 8 - 2][2];
+    p = new char [2][MAX / 8 - 3][2];
+
+    p = new char [MAX][2][2];           // { dg-error "size of array" }
+    p = new char [MAX - 1][2][2];       // { dg-error "size of array" }
+    p = new char [MAX - 2][2][2];       // { dg-error "size of array" }
+    p = new char [MAX - 99][2][2];      // { dg-error "size of array" }
+    p = new char [MAX / 2][2][2];       // { dg-error "size of array" }
+    p = new char [MAX / 2 - 1][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 2][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 3][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 4][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 5][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 6][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 7][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 8][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 4][2][2];       // { dg-error "size of array" }
+    p = new char [MAX / 4 - 1][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 4 - 2][2][2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [MAX / 8][2][2];
+    // p = new char [MAX / 8 - 1][2][2];
+
+    p = new char [MAX / 8 - 2][2][2];
+    p = new char [MAX / 8 - 3][2][2];
+
+    p = new char [MAX][MAX][MAX];         // { dg-error "size of array" }
+    p = new char [MAX][MAX][MAX / 2];     // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2][MAX];     // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2][MAX / 2]; // { dg-error "size of array" }
+    p = new char [MAX / 2][MAX / 2][MAX / 2]; // { dg-error "size of array" }
+}
+
+// Exercise new expression with N-dimensional arrays where N is
+// sizeof(size_t).
+static __attribute__ ((used)) void
+test_N_dim_char_array ()
+{
+#if __SIZEOF_SIZE_T__ == 8
+    enum { N = 256 };
+#else
+    enum { N = 16 };
+#endif
+
+    p = new char        [N][N][N][N][N][N][N];
+    p = new char [N / 2][2][N][N][N][N][N][N];
+    p = new char [N - 1][N / 2][N][N][N][N][N][N];
+    p = new char [N / 2][N][N][N][N][N][N][N];  // { dg-error "size of array" }
+    p = new char [N - 1][N][N][N][N][N][N][N];  // { dg-error "size of array" }
+    p = new char [N]    [N][N][N][N][N][N][N];  // { dg-error "size of array" }
+}
+
+typedef struct Byte {
+    char c;
+
+    void* operator new (size_t, void*);
+    void* operator new[] (size_t, void*);
+} B;
+
+void* operator new (size_t, B*);
+void* operator new[] (size_t, B*);
+
+// Exercise placement new expression with one-dimensional arrays of a struct.
+static void __attribute__ ((used))
+test_one_dim_byte_array (void *p)
+{
+    p = new (p) B [MAX];                // { dg-error "size of array" }
+    p = new (p) B [MAX - 1];            // { dg-error "size of array" }
+    p = new (p) B [MAX - 2];            // { dg-error "size of array" }
+    p = new (p) B [MAX - 99];           // { dg-error "size of array" }
+    p = new (p) B [MAX / 2];            // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 1];        // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 2];        // { dg-error "size of array" }
+
+    // Avoid testing the expressions below since whether or not they
+    // are accepted depends on the precision of size_t (which determines
+    // the size of the cookie).
+    // p = new (p) B [MAX / 2 - 3];
+    // p = new (p) B [MAX / 2 - 4];
+    // p = new (p) B [MAX / 2 - 5];
+    // p = new (p) B [MAX / 2 - 6];
+
+    // The following expressions are accepted on ILP32 as well LP64
+    // (they will be diagnosed on LP128 if there ever is such a data
+    // model).
+    p = new (p) B [MAX / 2 - 7];         // okay
+    p = new (p) B [MAX / 2 - 8];         // okay
+}
+
+// Exercise placement new expression with two-dimensional arrays.
+static void __attribute__ ((used))
+test_placement_two_dim_byte_struct_array (void *p)
+{
+    p = new (p) B [1][MAX];             // { dg-error "size of array" }
+    p = new (p) B [1][MAX - 1];         // { dg-error "size of array" }
+    p = new (p) B [1][MAX - 2];         // { dg-error "size of array" }
+    p = new (p) B [1][MAX - 99];        // { dg-error "size of array" }
+    p = new (p) B [1][MAX / 2];         // { dg-error "size of array" }
+    p = new (p) B [1][MAX / 2 - 1];     // { dg-error "size of array" }
+    p = new (p) B [1][MAX / 2 - 2];     // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [1][MAX / 2 - 3];
+    // p = new (p) B [1][MAX / 2 - 4];
+    // p = new (p) B [1][MAX / 2 - 5];
+    // p = new (p) B [1][MAX / 2 - 6];
+
+    p = new (p) B [1][MAX / 2 - 7];      // okay
+    p = new (p) B [1][MAX / 2 - 8];      // okay
+
+    p = new (p) B [2][MAX];             // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 1];         // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 2];         // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2];         // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 1];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 2];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 7];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 8];     // { dg-error "size of array" }
+
+    p = new (p) B [MAX][MAX];           // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX - 1];       // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX - 2];       // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2];       // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new (p) B [MAX][2];             // { dg-error "size of array" }
+    p = new (p) B [MAX][1];             // { dg-error "size of array" }
+    p = new (p) B [MAX / 2][1];         // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 1][1];     // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 2][1];     // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [MAX / 2 - 3][1];
+    // p = new (p) B [MAX / 2 - 4][1];
+    // p = new (p) B [MAX / 2 - 5][1];
+    // p = new (p) B [MAX / 2 - 6][1];
+
+    p = new (p) B [MAX / 2 - 7][1];      // okay
+    p = new (p) B [MAX / 2 - 8][1];      // okay
+}
+
+
+// Exercise placement new expression with three-dimensional arrays.
+static __attribute__ ((used)) void
+test_placement_three_dim_byte_struct_array (void *p)
+{
+    p = new (p) B [1][1][MAX];          // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX / 2 - 2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [1][1][MAX / 2 - 3];
+    // p = new (p) B [1][1][MAX / 2 - 4];
+    // p = new (p) B [1][1][MAX / 2 - 5];
+    // p = new (p) B [1][1][MAX / 2 - 6];
+
+    p = new (p) B [1][1][MAX / 2 - 7];   // okay
+    p = new (p) B [1][1][MAX / 2 - 8];   // okay
+
+    p = new (p) B [1][2][MAX];          // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 2];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 3];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 4];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 5];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 6];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 7];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 8];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 4];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [1][2][MAX / 4 - 1];
+    // p = new (p) B [1][2][MAX / 4 - 2];
+
+    p = new (p) B [1][2][MAX / 4 - 3];   // okay
+    p = new (p) B [1][2][MAX / 4 - 4];   // okay
+
+    p = new (p) B [2][1][MAX];          // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 2];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 3];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 4];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 5];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 6];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 7];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 8];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 4];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [2][1][MAX / 4 - 1];
+    // p = new (p) B [2][1][MAX / 4 - 2];
+
+    p = new (p) B [2][1][MAX / 4 - 3];   // okay
+    p = new (p) B [2][1][MAX / 4 - 4];   // okay
+
+    p = new (p) B [2][2][MAX];          // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 2];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 3];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 4];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 5];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 6];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 7];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 8];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 4];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 4 - 1];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 4 - 2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [2][2][MAX / 8];
+    // p = new (p) B [2][2][MAX / 8 - 1];
+
+    p = new (p) B [2][2][MAX / 8 - 2];
+    p = new (p) B [2][2][MAX / 8 - 3];
+
+    p = new (p) B [2][MAX][2];          // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 1][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 2][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 99][2];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 1][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 2][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 3][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 4][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 5][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 6][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 7][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 8][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 4][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 4 - 1][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 4 - 2][2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [2][MAX / 8][2];
+    // p = new (p) B [2][MAX / 8 - 1][2];
+
+    p = new (p) B [2][MAX / 8 - 2][2];
+    p = new (p) B [2][MAX / 8 - 3][2];
+
+    p = new (p) B [MAX][2][2];          // { dg-error "size of array" }
+    p = new (p) B [MAX - 1][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX - 2][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX - 99][2][2];     // { dg-error "size of array" }
+    p = new (p) B [MAX / 2][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 1][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 2][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 3][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 4][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 5][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 6][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 7][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 8][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 4][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX / 4 - 1][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 4 - 2][2][2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [MAX / 8][2][2];
+    // p = new (p) B [MAX / 8 - 1][2][2];
+
+    p = new (p) B [MAX / 8 - 2][2][2];
+    p = new (p) B [MAX / 8 - 3][2][2];
+}
diff --git a/gcc/testsuite/g++.dg/init/new45.C b/gcc/testsuite/g++.dg/init/new45.C
new file mode 100644
index 0000000..92dac18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new45.C
@@ -0,0 +1,106 @@
+// { dg-do compile }
+// { dg-options "-O1" }
+// { dg-final { scan-assembler-not "abort" } }
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C" {
+    void abort ();
+    void* malloc (size_t);
+}
+
+struct UDClass {
+    static int n;
+    UDClass () { ++n; }
+    virtual ~UDClass () { --n; }
+};
+
+int UDClass::n;
+
+struct POD {
+    char buf [sizeof (UDClass)];
+};
+
+enum { N = 123 };
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n)
+{
+    // Verify that array new is invoked with an argument large enough
+    // for the array and a size_t cookie to store the number of elements.
+    // (This holds for classes with user-defined types but not POD types).
+    if (n != N * sizeof (UDClass) + sizeof n) abort ();
+    return malloc (n);
+}
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n, void *p)
+{
+    // Verify that the default placement array new is invoked with
+    // an argument just large enough for the array (and no cookie),
+    // regardless of whether the type is a POD or class with a user
+    // defined ctor.
+    if (n != N * sizeof (UDClass)) abort ();
+    return p;
+}
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n, POD *p)
+{
+    // Verify that placement array new overload for a POD type is
+    // invoked with an argument large enough for the array and
+    // a cookie.
+    if (n != N * sizeof (POD)) abort ();
+    return p;
+}
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n, UDClass *p)
+{
+    // Verify that placement array new overload for a class type with
+    // a user-defined ctor and dtor is invoked with an argument large
+    // enough for the array and a cookie.
+    if (n != N * sizeof (UDClass) + sizeof n) abort ();
+    return p;
+}
+
+// UDClassllocate a sufficiently large buffer to construct arrays into.
+static unsigned char buf [N * N];
+
+POD* test_new_POD ()
+{
+    // Avoid testing PODs since for those, the global new is invoked
+    // without the overhead of a cookie.
+    // return new POD [N];
+    return 0;
+}
+
+POD* test_default_placement_new_POD ()
+{
+    // Vefify that no overhead is allocated.
+    return new (buf) POD [N];
+}
+
+POD* test_overloaded_placement_new_POD ()
+{
+    // Vefify that no overhead is allocated.
+    return new ((POD*)buf) POD [N];
+}
+
+UDClass* test_new_UDClass ()
+{
+    // Vefify that space for a cookie is allocated.
+    return new UDClass [N];
+}
+
+UDClass* test_default_placement_new_UDClass ()
+{
+    // Vefify that no overhead is allocated.
+    return new (buf) UDClass [N];
+}
+
+UDClass* test_overloaded_placement_new_UDClass ()
+{
+    // Vefify that space for a cookie is allocated.
+    return new ((UDClass*)buf) UDClass [N];
+}
diff --git a/gcc/testsuite/g++.dg/other/new-size-type.C b/gcc/testsuite/g++.dg/other/new-size-type.C
index 04933fd..ad4dc10 100644
--- a/gcc/testsuite/g++.dg/other/new-size-type.C
+++ b/gcc/testsuite/g++.dg/other/new-size-type.C
@@ -5,6 +5,5 @@
 const char*
 foo()
 {
-    return new char[~static_cast<size_t>(0)];// { dg-bogus "large" }
+    return new char[~static_cast<size_t>(0)];// { dg-error "size of array" }
 }
-

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

* [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-10-19 23:06 [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements Martin Sebor
@ 2015-10-27  2:47 ` Martin Sebor
  2015-11-03  4:55   ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Martin Sebor @ 2015-10-27  2:47 UTC (permalink / raw)
  To: Gcc Patch List; +Cc: jason

[CC Jason]

The patch is at the link below:
https://gcc.gnu.org/ml/gcc-patches/2015-10/msg01803.html

Thanks

On 10/19/2015 12:50 PM, Martin Sebor wrote:
> This is a patch for two C++ bugs:
>
>    67913 - new expression with negative size not diagnosed
>    67927 - array new expression with excessive number of elements
>            not diagnosed
>
> The C++ front end rejects a subset of array declarators with negative
> bounds or with bounds in excess of some implementation defined maximum
> (roughly SIZE_MAX / 2), but it does so inconsistently.  For example,
> it silently accepts expressions such as
>
>      new int [-1][2];
>
> but invokes operator new to allocate SIZE_MAX bytes.  When operator
> new succeeds (as is the case on Linux with memory overcommitment
> enabled), the new expression returns a valid pointer to some
> unknown amount of storage less than SIZE_MAX.  Accessing the memory
> at some non-zero offset then causes a SIGSEGV.
>
> Similarly, GCC accepts the following expression with the same result:
>
>      new int [SIZE_MAX][2];
>
> C++ 14 makes it clear that such expressions are ill-formed and must
> be rejected.  This patch adds checks that consistently reject all
> new expressions with both negative array bounds and bounds in excess
> of the maximum.
>
> While I raised these bugs as separate issues I decided to group the
> two sets of changes together since they both touch the same function
> in similar ways, and hopefully doing so will make them also easier
> to review.
>
> I've tested the patch by bootstrapping C/C++ and running the test
> suites (including libstdc++) on x86_64 with no regressions.
>
> During the development of the changes I found a few basic mistakes
> in my code only after running libstdc++ tests.  To make it possible
> to uncover them sooner in the future, I added another test that
> isn't directly related to the problem: new45.C.
>
> I also found a minor problem in the GCC regression test suite where
> the g++.dg/other/new-size-type.C test for PR 36741 tried to check
> that the 'new char[~static_cast<size_t>(0)]' expression was accepted
> without a warning.  The complaint in the PR was about the wording of
> the warning, not about the validity of the expression (the submitter
> agreed that a correctly worded diagnostic would be appropriate).
>   I chaged the test to expect a meaningful error message.
>
> Once this patch is approved and committed a follow-up patch should
> document the implementation-defined maximum to the manual.
>
> Martin

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-10-27  2:47 ` [PING] " Martin Sebor
@ 2015-11-03  4:55   ` Jason Merrill
  2015-11-05 19:30     ` Martin Sebor
  0 siblings, 1 reply; 9+ messages in thread
From: Jason Merrill @ 2015-11-03  4:55 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 10/26/2015 10:06 PM, Martin Sebor wrote:
> +      if (TREE_CONSTANT (maybe_constant_value (outer_nelts)))
> +	{
> +	  if (tree_int_cst_lt (max_outer_nelts_tree, outer_nelts))

maybe_constant_value may return a constant, but that doesn't mean that 
outer_nelts was already constant; if it wasn't, the call to 
tree_int_cst_lt will fail.

> +      tree nelts_save = nelts;
> +      nelts = maybe_constant_value (nelts);
> +
> +      if (!TREE_CONSTANT (nelts))
> +	nelts = nelts_save;
> +
> +      /* The expression in a noptr-new-declarator is erroneous if it's of
> +	 non-class type and its value before converting to std::size_t is
> +	 less than zero. ... If the expression is a constant expression,
> +	 the program is ill-fomed.  */
> +      if (TREE_CONSTANT (nelts) && tree_int_cst_sgn (nelts) == -1)
> +	{
> +	  if (complain & tf_error)
> +	    error ("size of array is negative");
> +	  return error_mark_node;
> +	}

Since we're moving toward delayed folding, I'd prefer to use the result 
of maybe_constant_value only for this diagnostic, and then continue to 
pass the unfolded value along.

Jason

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-11-03  4:55   ` Jason Merrill
@ 2015-11-05 19:30     ` Martin Sebor
  2015-11-06 14:21       ` Jason Merrill
  2015-11-26 10:23       ` James Greenhalgh
  0 siblings, 2 replies; 9+ messages in thread
From: Martin Sebor @ 2015-11-05 19:30 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

On 11/02/2015 09:55 PM, Jason Merrill wrote:
> On 10/26/2015 10:06 PM, Martin Sebor wrote:
>> +      if (TREE_CONSTANT (maybe_constant_value (outer_nelts)))
>> +    {
>> +      if (tree_int_cst_lt (max_outer_nelts_tree, outer_nelts))
>
> maybe_constant_value may return a constant, but that doesn't mean that
> outer_nelts was already constant; if it wasn't, the call to
> tree_int_cst_lt will fail.

Thanks for the hint. I wasn't able to trigger the failure. I suspect
outer_nelts must have already been folded at this point because the
maybe_constant_value call isn't necessary. I removed it.

> Since we're moving toward delayed folding, I'd prefer to use the result
> of maybe_constant_value only for this diagnostic, and then continue to
> pass the unfolded value along.

Sure. Done in the attached patch.

Martin

[-- Attachment #2: gcc-67913-67917-array-new-wrong-number-of-elements.patch --]
[-- Type: text/x-patch, Size: 44919 bytes --]

gcc/cp/ChangeLog

2015-10-19  Martin Sebor  <msebor@redhat.com>

	PR c++/67913
	PR c++/67927
	* call.c (build_operator_new_call): Do not assume size_check
	is non-null, analogously to the top half of the function.
	* init.c (build_new_1): Detect and diagnose array sizes in
	excess of the maximum of roughly SIZE_MAX / 2.
	Insert a runtime check only for arrays with a non-constant size.
	(build_new): Detect and diagnose negative array sizes.

gcc/testsuite/ChangeLog

2015-10-19  Martin Sebor  <msebor@redhat.com>

	* init/new45.C: New test to verify that operator new is invoked
	with or without overhead for a cookie.

	PR c++/67927
	* init/new44.C: New test for placement new expressions for arrays
	with excessive number of elements.

	PR c++/67913
	* init/new43.C: New test for placement new expressions for arrays
	with negative number of elements.

	* other/new-size-type.C: Expect array new expression with
	an excessive number of elements to be rejected.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 367d42b..3f76198 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4228,10 +4228,12 @@ build_operator_new_call (tree fnname, vec<tree, va_gc> **args,
 	 {
 	   /* Update the total size.  */
 	   *size = size_binop (PLUS_EXPR, original_size, *cookie_size);
+	   if (size_check)
+	     {
 	       /* Set to (size_t)-1 if the size check fails.  */
-	   gcc_assert (size_check != NULL_TREE);
 	       *size = fold_build3 (COND_EXPR, sizetype, size_check,
 				    *size, TYPE_MAX_VALUE (sizetype));
+	     }
 	   /* Update the argument list to reflect the adjusted size.  */
 	   (**args)[0] = *size;
 	 }
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..3b88098 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2272,7 +2272,11 @@ throw_bad_array_new_length (void)
 /* Generate code for a new-expression, including calling the "operator
    new" function, initializing the object, and, if an exception occurs
    during construction, cleaning up.  The arguments are as for
-   build_raw_new_expr.  This may change PLACEMENT and INIT.  */
+   build_raw_new_expr.  This may change PLACEMENT and INIT.
+   TYPE is the type of the object being constructed, possibly an array
+   of NELTS elements when NELTS is non-null (in "new T[NELTS]", T may
+   be an array of the form U[inner], with the whole expression being
+   "new U[NELTS][inner]").  */

 static tree
 build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
@@ -2292,13 +2296,16 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
      type.)  */
   tree pointer_type;
   tree non_const_pointer_type;
+  /* The most significant array bound in int[OUTER_NELTS][inner].  */
   tree outer_nelts = NULL_TREE;
-  /* For arrays, a bounds checks on the NELTS parameter. */
+  /* For arrays with a non-constant number of elements, a bounds checks
+     on the NELTS parameter to avoid integer overflow at runtime. */
   tree outer_nelts_check = NULL_TREE;
   bool outer_nelts_from_type = false;
+  /* Number of the "inner" elements in "new T[OUTER_NELTS][inner]".  */
   offset_int inner_nelts_count = 1;
   tree alloc_call, alloc_expr;
-  /* Size of the inner array elements. */
+  /* Size of the inner array elements (those with constant dimensions). */
   offset_int inner_size;
   /* The address returned by the call to "operator new".  This node is
      a VAR_DECL and is therefore reusable.  */
@@ -2492,21 +2499,41 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 	}

       max_outer_nelts = wi::udiv_trunc (max_size, inner_size);
-      /* Only keep the top-most seven bits, to simplify encoding the
-	 constant in the instruction stream.  */
+      max_outer_nelts_tree = wide_int_to_tree (sizetype, max_outer_nelts);
+
+      size = size_binop (MULT_EXPR, size, convert (sizetype, nelts));
+
+      if (TREE_CONSTANT (outer_nelts))
+	{
+	  if (tree_int_cst_lt (max_outer_nelts_tree, outer_nelts))
+	    {
+	      /* When the array size is constant, check it at compile time
+		 to make sure it doesn't exceed the implementation-defined
+		 maximum, as required by C++ 14 (in C++ 11 this requirement
+		 isn't explicitly stated but it's enforced anyway -- see
+		 grokdeclarator in cp/decl.c).  */
+	      if (complain & tf_error)
+		error ("size of array is too large");
+	      return error_mark_node;
+	    }
+	}
+      else
 	{
+	  /* When a runtime check is necessary because the array size
+	     isn't constant, keep only the top-most seven bits (starting
+	     with the most significant non-zero bit) of the maximum size
+	     to compare the array size against, to simplify encoding the
+	     constant maximum size in the instruction stream.  */
 	  unsigned shift = (max_outer_nelts.get_precision ()) - 7
 	    - wi::clz (max_outer_nelts);
 	  max_outer_nelts = wi::lshift (wi::lrshift (max_outer_nelts, shift),
 					shift);
-      }
-      max_outer_nelts_tree = wide_int_to_tree (sizetype, max_outer_nelts);

-      size = size_binop (MULT_EXPR, size, convert (sizetype, nelts));
 	  outer_nelts_check = fold_build2 (LE_EXPR, boolean_type_node,
 					   outer_nelts,
 					   max_outer_nelts_tree);
 	}
+    }

   alloc_fn = NULL_TREE;

@@ -3066,6 +3093,23 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
           else
             return error_mark_node;
         }
+
+      /* Try to determine the constant value only for the purposes
+	 of the diagnostic below but continue to use the original
+	 value and handle const folding later.  */
+      const_tree cst_nelts = maybe_constant_value (nelts);
+
+      /* The expression in a noptr-new-declarator is erroneous if it's of
+	 non-class type and its value before converting to std::size_t is
+	 less than zero. ... If the expression is a constant expression,
+	 the program is ill-fomed.  */
+      if (TREE_CONSTANT (cst_nelts) && tree_int_cst_sgn (cst_nelts) == -1)
+	{
+	  if (complain & tf_error)
+	    error ("size of array is negative");
+	  return error_mark_node;
+	}
+
       nelts = mark_rvalue_use (nelts);
       nelts = cp_save_expr (cp_convert (sizetype, nelts, complain));
     }
diff --git a/gcc/testsuite/g++.dg/init/new43.C b/gcc/testsuite/g++.dg/init/new43.C
new file mode 100644
index 0000000..9b08667
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new43.C
@@ -0,0 +1,164 @@
+/* { dg-do compile } */
+
+// Test for PR c++/67913 - new expression with negative size not diagnosed.
+typedef __typeof__ (sizeof 0) size_t;
+
+void* operator new (size_t, void*);
+void* operator new[] (size_t, void*);
+
+struct A {
+    int a [4];
+};
+
+struct B {
+    int a [4];
+
+    void* operator new (size_t, void*);
+    void* operator new[] (size_t, void*);
+};
+
+void* operator new (size_t, B*);
+void* operator new[] (size_t, B*);
+
+void *p;
+
+void test_literal ()
+{
+    char c;
+    (void)c;
+
+    B b;
+
+    // Verify integer literal.
+    p = new char [-1];           // { dg-error "size of array is negative" }
+    p = new char [2][-3];        // { dg-error "size of array is negative" }
+    p = new char [-4][5];        // { dg-error "size of array is negative" }
+    p = new char [-6][-7];       // { dg-error "size of array is negative" }
+
+    p = new (p) char [-1];       // { dg-error "size of array is negative" }
+    p = new (p) char [2][-3];    // { dg-error "size of array is negative" }
+    p = new (p) char [-4][5];    // { dg-error "size of array is negative" }
+    p = new (p) char [-6][-7];   // { dg-error "size of array is negative" }
+
+    p = new (p) A [-1];          // { dg-error "size of array is negative" }
+    p = new (p) A [2][-3];       // { dg-error "size of array is negative" }
+    p = new (p) A [-4][5];       // { dg-error "size of array is negative" }
+    p = new (p) A [-6][-7];      // { dg-error "size of array is negative" }
+
+    p = new (p) B [-1];          // { dg-error "size of array is negative" }
+    p = new (p) B [2][-3];       // { dg-error "size of array is negative" }
+    p = new (p) B [-4][5];       // { dg-error "size of array is negative" }
+    p = new (p) B [-6][-7];      // { dg-error "size of array is negative" }
+
+    p = new (&b) B [-1];          // { dg-error "size of array is negative" }
+    p = new (&b) B [2][-3];       // { dg-error "size of array is negative" }
+    p = new (&b) B [-4][5];       // { dg-error "size of array is negative" }
+    p = new (&b) B [-6][-7];      // { dg-error "size of array is negative" }
+
+    p = new char [1 - 2];         // { dg-error "size of array is negative" }
+    p = new (p) char [2 - 3];     // { dg-error "size of array is negative" }
+    p = new A [2 < 1 ? -1 : -2];  // { dg-error "size of array is negative" }
+    p = new (p) B [2 - 3 * 2];    // { dg-error "size of array is negative" }
+    p = new (&b) B [1][2 - 3 * 2];// { dg-error "size of array is negative" }
+}
+
+void test_constant_expression ()
+{
+    char c;
+    (void)c;
+
+    B b;
+
+    static const signed char i1 = -1;
+    static const signed short i2 = -2;
+    static const signed int i3 = -3;
+    static const signed long i4 = -4;
+    static const signed long long i5 = -5;
+    static const int i6 = -6;
+    static const int i7 = -7;
+
+    // Verify constant expression.
+    p = new char [i1];           // { dg-error "size of array is negative" }
+    p = new char [2][i3];        // { dg-error "size of array is negative" }
+    p = new char [i4][5];        // { dg-error "size of array is negative" }
+    p = new char [i6][i7];       // { dg-error "size of array is negative" }
+
+    p = new (p) char [i1];       // { dg-error "size of array is negative" }
+    p = new (p) char [2][i3];    // { dg-error "size of array is negative" }
+    p = new (p) char [i4][5];    // { dg-error "size of array is negative" }
+    p = new (p) char [i6][i7];   // { dg-error "size of array is negative" }
+
+    p = new (p) A [i1];          // { dg-error "size of array is negative" }
+    p = new (p) A [2][i3];       // { dg-error "size of array is negative" }
+    p = new (p) A [i4][5];       // { dg-error "size of array is negative" }
+    p = new (p) A [i6][i7];      // { dg-error "size of array is negative" }
+
+    p = new (p) B [i1];          // { dg-error "size of array is negative" }
+    p = new (p) B [2][i3];       // { dg-error "size of array is negative" }
+    p = new (p) B [i4][5];       // { dg-error "size of array is negative" }
+    p = new (p) B [i6][i7];      // { dg-error "size of array is negative" }
+
+    p = new (&b) B [i1];          // { dg-error "size of array is negative" }
+    p = new (&b) B [2][i3];       // { dg-error "size of array is negative" }
+    p = new (&b) B [i4][5];       // { dg-error "size of array is negative" }
+    p = new (&b) B [i6][i7];      // { dg-error "size of array is negative" }
+
+    p = new short [i1 - 2];       // { dg-error "size of array is negative" }
+    p = new (p) bool [i2 - 3];    // { dg-error "size of array is negative" }
+    p = new A [2 < 1 ? i1 : i2];  // { dg-error "size of array is negative" }
+    p = new (p) B [2 + i3 * 2];   // { dg-error "size of array is negative" }
+    p = new (&b) B [1][i1 - 3 * 2];// { dg-error "size of array is negative" }
+}
+
+void test_constexpr ()
+{
+    B b;
+
+#if __cplusplus >= 201103L
+
+    // Verify that a constant expression that is "a prvalue core constant
+    // expression whose value is an object where, for that object and its
+    // subobjects each non-static data member of reference type refers to
+    // an object with static storage duration."
+    static constexpr struct S {
+        int i_;
+        constexpr S (int i): i_ (i) { }
+        constexpr operator int () const { return i_; }
+    } s1 (-1), s2 (-2), s3 (-3), s4 (-4), s5 (-5), s6 (-6), s7 (-7);
+#else
+    // C++ 11 constexpr is not available, fall back on plain ole enum.
+    enum { s1 = -1, s2 = -2, s3 = -3, s4 = -4, s5 = -5, s6 = -6, s7 = -7 };
+#endif
+
+    // Verify constant expression.
+    p = new char [s1];           // { dg-error "size of array is negative" }
+    p = new char [2][s3];        // { dg-error "size of array is negative" }
+    p = new char [s4][5];        // { dg-error "size of array is negative" }
+    p = new char [s6][s7];       // { dg-error "size of array is negative" }
+
+    p = new (p) char [s1];       // { dg-error "size of array is negative" }
+    p = new (p) char [2][s3];    // { dg-error "size of array is negative" }
+    p = new (p) char [s4][5];    // { dg-error "size of array is negative" }
+    p = new (p) char [s6][s7];   // { dg-error "size of array is negative" }
+
+    p = new (p) A [s1];          // { dg-error "size of array is negative" }
+    p = new (p) A [2][s3];       // { dg-error "size of array is negative" }
+    p = new (p) A [s4][5];       // { dg-error "size of array is negative" }
+    p = new (p) A [s6][s7];      // { dg-error "size of array is negative" }
+
+    p = new (p) B [s1];          // { dg-error "size of array is negative" }
+    p = new (p) B [2][s3];       // { dg-error "size of array is negative" }
+    p = new (p) B [s4][5];       // { dg-error "size of array is negative" }
+    p = new (p) B [s6][s7];      // { dg-error "size of array is negative" }
+
+    p = new (&b) B [s1];          // { dg-error "size of array is negative" }
+    p = new (&b) B [2][s3];       // { dg-error "size of array is negative" }
+    p = new (&b) B [s4][5];       // { dg-error "size of array is negative" }
+    p = new (&b) B [s6][s7];      // { dg-error "size of array is negative" }
+
+    p = new int [s1 + s2];           // { dg-error "size of array is negative" }
+    p = new (p) long [2 * s3];       // { dg-error "size of array is negative" }
+    p = new A [s2 < s1 ? s1 : s2];   // { dg-error "size of array is negative" }
+    p = new (p) B [s7 - s2 * 2];     // { dg-error "size of array is negative" }
+    p = new (&b) B [9][s4 - s1 * 2]; // { dg-error "size of array is negative" }
+}
diff --git a/gcc/testsuite/g++.dg/init/new44.C b/gcc/testsuite/g++.dg/init/new44.C
new file mode 100644
index 0000000..d6ff86a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new44.C
@@ -0,0 +1,532 @@
+// { dg-do compile }
+
+// Test for PR c++/67927 - array new expression with excessive number
+// of elements not diagnosed.
+
+// GCC uses a different maximum value at compile time and at runtime:
+// 1) The compile-time maximum, MAX, is SIZE_MAX / 2 minus the size
+//    of a cookie (sizeof (size_t)).  Exceeding the compile-time
+//    maximum is ill-formed and diagnosed.  This test verifies this
+//    diagnostic.
+// 2) The runtime runtime maximum is the most significant 7 bits,
+//    starting with the first most significant non-zero bit, of
+//    the dividend of the compile-time constant MAX and the product
+//    of the constant array dimensions and the element size, minus
+//    the size of the "cookie."  This is also roughly (though not
+//    exactly) SIZE_MAX / 2.  Exceeding the runtime maximum is
+//    diagnosed at runtime by throwing a bad_array_new_length
+//    exception.
+//    The cookie is the number of elements in the array, and is
+//    only added for non-POD types, but the its size factors into
+//    the maximum size formula regardless.
+
+// See also PR c++/19351 - integer overflow in operator new[].
+
+// For convenience.
+#define MAX __SIZE_MAX__
+
+typedef __typeof__ (sizeof 0) size_t;
+
+void* operator new (size_t, void*);
+void* operator new[] (size_t, void*);
+
+void *p;
+
+// Exercise new expression with one-dimensional arrays of char.
+static void __attribute__ ((used))
+test_one_dim_char_array ()
+{
+    p = new char [MAX];                 // { dg-error "size of array" }
+    p = new char [MAX - 1];             // { dg-error "size of array" }
+    p = new char [MAX - 2];             // { dg-error "size of array" }
+    p = new char [MAX - 99];            // { dg-error "size of array" }
+    p = new char [MAX / 2];             // { dg-error "size of array" }
+    p = new char [MAX / 2 - 1];         // { dg-error "size of array" }
+    p = new char [MAX / 2 - 2];         // { dg-error "size of array" }
+
+    // Avoid testing the expressions below since whether or not they
+    // are accepted depends on the precision of size_t (which also
+    // determines the size of the cookie).
+    // p = new char [MAX / 2 - 3];
+    // p = new char [MAX / 2 - 4];
+    // p = new char [MAX / 2 - 5];
+    // p = new char [MAX / 2 - 6];
+
+    // The following expressions are accepted on ILP32 as well LP64
+    // (they will be diagnosed on LP128 if there ever is such a data
+    // model).
+    p = new char [MAX / 2 - 7];         // okay
+    p = new char [MAX / 2 - 8];         // okay
+}
+
+static void __attribute__ ((used))
+test_one_dim_short_array ()
+{
+    p = new short [MAX];                // { dg-error "size of array" }
+    p = new short [MAX - 1];            // { dg-error "size of array" }
+    p = new short [MAX - 2];            // { dg-error "size of array" }
+    p = new short [MAX - 99];           // { dg-error "size of array" }
+    p = new short [MAX / 2];            // { dg-error "size of array" }
+    p = new short [MAX / 2 - 1];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 2];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 3];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 4];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 5];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 6];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 7];        // { dg-error "size of array" }
+    p = new short [MAX / 2 - 8];        // { dg-error "size of array" }
+    p = new short [MAX / 4];            // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new short [MAX / 4 - 1];
+
+    p = new short [MAX / 4 - 4];        // okay
+}
+
+// Exercise new expression with two-dimensional arrays or char.
+static void __attribute__ ((used))
+test_two_dim_char_array ()
+{
+    p = new char [1][MAX];              // { dg-error "size of array" }
+    p = new char [1][MAX - 1];          // { dg-error "size of array" }
+    p = new char [1][MAX - 2];          // { dg-error "size of array" }
+    p = new char [1][MAX - 99];         // { dg-error "size of array" }
+    p = new char [1][MAX / 2];          // { dg-error "size of array" }
+    p = new char [1][MAX / 2 - 1];      // { dg-error "size of array" }
+    p = new char [1][MAX / 2 - 2];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [1][MAX / 2 - 3];
+    // p = new char [1][MAX / 2 - 4];
+    // p = new char [1][MAX / 2 - 5];
+    // p = new char [1][MAX / 2 - 6];
+
+    p = new char [1][MAX / 2 - 7];      // okay
+    p = new char [1][MAX / 2 - 8];      // okay
+
+    p = new char [2][MAX];              // { dg-error "size of array" }
+    p = new char [2][MAX - 1];          // { dg-error "size of array" }
+    p = new char [2][MAX - 2];          // { dg-error "size of array" }
+    p = new char [2][MAX / 2];          // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 1];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 2];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 7];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 8];      // { dg-error "size of array" }
+
+    p = new char [MAX][MAX];            // { dg-error "size of array" }
+    p = new char [MAX][MAX - 1];        // { dg-error "size of array" }
+    p = new char [MAX][MAX - 2];        // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2];        // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 1];    // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 2];    // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 7];    // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2 - 8];    // { dg-error "size of array" }
+    p = new char [MAX][2];              // { dg-error "size of array" }
+    p = new char [MAX][1];              // { dg-error "size of array" }
+    p = new char [MAX / 2][1];          // { dg-error "size of array" }
+    p = new char [MAX / 2 - 1][1];      // { dg-error "size of array" }
+    p = new char [MAX / 2 - 2][1];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [MAX / 2 - 3][1];
+    // p = new char [MAX / 2 - 4][1];
+    // p = new char [MAX / 2 - 5][1];
+    // p = new char [MAX / 2 - 6][1];
+
+    p = new char [MAX / 2 - 7][1];      // okay
+    p = new char [MAX / 2 - 8][1];      // okay
+}
+
+
+// Exercise new expression with three-dimensional arrays.
+static __attribute__ ((used)) void
+test_three_dim_char_array ()
+{
+    p = new char [1][1][MAX];           // { dg-error "size of array" }
+    p = new char [1][1][MAX - 1];       // { dg-error "size of array" }
+    p = new char [1][1][MAX - 2];       // { dg-error "size of array" }
+    p = new char [1][1][MAX - 99];      // { dg-error "size of array" }
+    p = new char [1][1][MAX / 2];       // { dg-error "size of array" }
+    p = new char [1][1][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [1][1][MAX / 2 - 2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [1][1][MAX / 2 - 3];
+    // p = new char [1][1][MAX / 2 - 4];
+    // p = new char [1][1][MAX / 2 - 5];
+    // p = new char [1][1][MAX / 2 - 6];
+
+    p = new char [1][1][MAX / 2 - 7];   // okay
+    p = new char [1][1][MAX / 2 - 8];   // okay
+
+    p = new char [1][2][MAX];           // { dg-error "size of array" }
+    p = new char [1][2][MAX - 1];       // { dg-error "size of array" }
+    p = new char [1][2][MAX - 2];       // { dg-error "size of array" }
+    p = new char [1][2][MAX - 99];      // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2];       // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 3];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 4];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 5];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 6];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new char [1][2][MAX / 4];       // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [1][2][MAX / 4 - 1];
+    // p = new char [1][2][MAX / 4 - 2];
+
+    p = new char [1][2][MAX / 4 - 3];   // okay
+    p = new char [1][2][MAX / 4 - 4];   // okay
+
+    p = new char [2][1][MAX];           // { dg-error "size of array" }
+    p = new char [2][1][MAX - 1];       // { dg-error "size of array" }
+    p = new char [2][1][MAX - 2];       // { dg-error "size of array" }
+    p = new char [2][1][MAX - 99];      // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2];       // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 3];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 4];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 5];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 6];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new char [2][1][MAX / 4];       // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [2][1][MAX / 4 - 1];
+    // p = new char [2][1][MAX / 4 - 2];
+
+    p = new char [2][1][MAX / 4 - 3];   // okay
+    p = new char [2][1][MAX / 4 - 4];   // okay
+
+    p = new char [2][2][MAX];           // { dg-error "size of array" }
+    p = new char [2][2][MAX - 1];       // { dg-error "size of array" }
+    p = new char [2][2][MAX - 2];       // { dg-error "size of array" }
+    p = new char [2][2][MAX - 99];      // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2];       // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 3];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 4];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 5];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 6];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 4];       // { dg-error "size of array" }
+    p = new char [2][2][MAX / 4 - 1];   // { dg-error "size of array" }
+    p = new char [2][2][MAX / 4 - 2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [2][2][MAX / 8];
+    // p = new char [2][2][MAX / 8 - 1];
+
+    p = new char [2][2][MAX / 8 - 2];
+    p = new char [2][2][MAX / 8 - 3];
+
+    p = new char [2][MAX][2];           // { dg-error "size of array" }
+    p = new char [2][MAX - 1][2];       // { dg-error "size of array" }
+    p = new char [2][MAX - 2][2];       // { dg-error "size of array" }
+    p = new char [2][MAX - 99][2];      // { dg-error "size of array" }
+    p = new char [2][MAX / 2][2];       // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 1][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 2][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 3][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 4][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 5][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 6][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 7][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 2 - 8][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 4][2];       // { dg-error "size of array" }
+    p = new char [2][MAX / 4 - 1][2];   // { dg-error "size of array" }
+    p = new char [2][MAX / 4 - 2][2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [2][MAX / 8][2];
+    // p = new char [2][MAX / 8 - 1][2];
+
+    p = new char [2][MAX / 8 - 2][2];
+    p = new char [2][MAX / 8 - 3][2];
+
+    p = new char [MAX][2][2];           // { dg-error "size of array" }
+    p = new char [MAX - 1][2][2];       // { dg-error "size of array" }
+    p = new char [MAX - 2][2][2];       // { dg-error "size of array" }
+    p = new char [MAX - 99][2][2];      // { dg-error "size of array" }
+    p = new char [MAX / 2][2][2];       // { dg-error "size of array" }
+    p = new char [MAX / 2 - 1][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 2][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 3][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 4][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 5][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 6][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 7][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 2 - 8][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 4][2][2];       // { dg-error "size of array" }
+    p = new char [MAX / 4 - 1][2][2];   // { dg-error "size of array" }
+    p = new char [MAX / 4 - 2][2][2];   // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new char [MAX / 8][2][2];
+    // p = new char [MAX / 8 - 1][2][2];
+
+    p = new char [MAX / 8 - 2][2][2];
+    p = new char [MAX / 8 - 3][2][2];
+
+    p = new char [MAX][MAX][MAX];         // { dg-error "size of array" }
+    p = new char [MAX][MAX][MAX / 2];     // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2][MAX];     // { dg-error "size of array" }
+    p = new char [MAX][MAX / 2][MAX / 2]; // { dg-error "size of array" }
+    p = new char [MAX / 2][MAX / 2][MAX / 2]; // { dg-error "size of array" }
+}
+
+// Exercise new expression with N-dimensional arrays where N is
+// sizeof(size_t).
+static __attribute__ ((used)) void
+test_N_dim_char_array ()
+{
+#if __SIZEOF_SIZE_T__ == 8
+    enum { N = 256 };
+#else
+    enum { N = 16 };
+#endif
+
+    p = new char        [N][N][N][N][N][N][N];
+    p = new char [N / 2][2][N][N][N][N][N][N];
+    p = new char [N - 1][N / 2][N][N][N][N][N][N];
+    p = new char [N / 2][N][N][N][N][N][N][N];  // { dg-error "size of array" }
+    p = new char [N - 1][N][N][N][N][N][N][N];  // { dg-error "size of array" }
+    p = new char [N]    [N][N][N][N][N][N][N];  // { dg-error "size of array" }
+}
+
+typedef struct Byte {
+    char c;
+
+    void* operator new (size_t, void*);
+    void* operator new[] (size_t, void*);
+} B;
+
+void* operator new (size_t, B*);
+void* operator new[] (size_t, B*);
+
+// Exercise placement new expression with one-dimensional arrays of a struct.
+static void __attribute__ ((used))
+test_one_dim_byte_array (void *p)
+{
+    p = new (p) B [MAX];                // { dg-error "size of array" }
+    p = new (p) B [MAX - 1];            // { dg-error "size of array" }
+    p = new (p) B [MAX - 2];            // { dg-error "size of array" }
+    p = new (p) B [MAX - 99];           // { dg-error "size of array" }
+    p = new (p) B [MAX / 2];            // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 1];        // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 2];        // { dg-error "size of array" }
+
+    // Avoid testing the expressions below since whether or not they
+    // are accepted depends on the precision of size_t (which determines
+    // the size of the cookie).
+    // p = new (p) B [MAX / 2 - 3];
+    // p = new (p) B [MAX / 2 - 4];
+    // p = new (p) B [MAX / 2 - 5];
+    // p = new (p) B [MAX / 2 - 6];
+
+    // The following expressions are accepted on ILP32 as well LP64
+    // (they will be diagnosed on LP128 if there ever is such a data
+    // model).
+    p = new (p) B [MAX / 2 - 7];         // okay
+    p = new (p) B [MAX / 2 - 8];         // okay
+}
+
+// Exercise placement new expression with two-dimensional arrays.
+static void __attribute__ ((used))
+test_placement_two_dim_byte_struct_array (void *p)
+{
+    p = new (p) B [1][MAX];             // { dg-error "size of array" }
+    p = new (p) B [1][MAX - 1];         // { dg-error "size of array" }
+    p = new (p) B [1][MAX - 2];         // { dg-error "size of array" }
+    p = new (p) B [1][MAX - 99];        // { dg-error "size of array" }
+    p = new (p) B [1][MAX / 2];         // { dg-error "size of array" }
+    p = new (p) B [1][MAX / 2 - 1];     // { dg-error "size of array" }
+    p = new (p) B [1][MAX / 2 - 2];     // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [1][MAX / 2 - 3];
+    // p = new (p) B [1][MAX / 2 - 4];
+    // p = new (p) B [1][MAX / 2 - 5];
+    // p = new (p) B [1][MAX / 2 - 6];
+
+    p = new (p) B [1][MAX / 2 - 7];      // okay
+    p = new (p) B [1][MAX / 2 - 8];      // okay
+
+    p = new (p) B [2][MAX];             // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 1];         // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 2];         // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2];         // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 1];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 2];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 7];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 8];     // { dg-error "size of array" }
+
+    p = new (p) B [MAX][MAX];           // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX - 1];       // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX - 2];       // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2];       // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 1];   // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 2];   // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 7];   // { dg-error "size of array" }
+    p = new (p) B [MAX][MAX / 2 - 8];   // { dg-error "size of array" }
+    p = new (p) B [MAX][2];             // { dg-error "size of array" }
+    p = new (p) B [MAX][1];             // { dg-error "size of array" }
+    p = new (p) B [MAX / 2][1];         // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 1][1];     // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 2][1];     // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [MAX / 2 - 3][1];
+    // p = new (p) B [MAX / 2 - 4][1];
+    // p = new (p) B [MAX / 2 - 5][1];
+    // p = new (p) B [MAX / 2 - 6][1];
+
+    p = new (p) B [MAX / 2 - 7][1];      // okay
+    p = new (p) B [MAX / 2 - 8][1];      // okay
+}
+
+
+// Exercise placement new expression with three-dimensional arrays.
+static __attribute__ ((used)) void
+test_placement_three_dim_byte_struct_array (void *p)
+{
+    p = new (p) B [1][1][MAX];          // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [1][1][MAX / 2 - 2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [1][1][MAX / 2 - 3];
+    // p = new (p) B [1][1][MAX / 2 - 4];
+    // p = new (p) B [1][1][MAX / 2 - 5];
+    // p = new (p) B [1][1][MAX / 2 - 6];
+
+    p = new (p) B [1][1][MAX / 2 - 7];   // okay
+    p = new (p) B [1][1][MAX / 2 - 8];   // okay
+
+    p = new (p) B [1][2][MAX];          // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 2];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 3];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 4];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 5];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 6];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 7];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 2 - 8];  // { dg-error "size of array" }
+    p = new (p) B [1][2][MAX / 4];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [1][2][MAX / 4 - 1];
+    // p = new (p) B [1][2][MAX / 4 - 2];
+
+    p = new (p) B [1][2][MAX / 4 - 3];   // okay
+    p = new (p) B [1][2][MAX / 4 - 4];   // okay
+
+    p = new (p) B [2][1][MAX];          // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 2];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 3];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 4];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 5];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 6];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 7];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 2 - 8];  // { dg-error "size of array" }
+    p = new (p) B [2][1][MAX / 4];      // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [2][1][MAX / 4 - 1];
+    // p = new (p) B [2][1][MAX / 4 - 2];
+
+    p = new (p) B [2][1][MAX / 4 - 3];   // okay
+    p = new (p) B [2][1][MAX / 4 - 4];   // okay
+
+    p = new (p) B [2][2][MAX];          // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX - 1];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX - 2];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX - 99];     // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 1];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 2];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 3];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 4];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 5];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 6];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 7];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 2 - 8];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 4];      // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 4 - 1];  // { dg-error "size of array" }
+    p = new (p) B [2][2][MAX / 4 - 2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [2][2][MAX / 8];
+    // p = new (p) B [2][2][MAX / 8 - 1];
+
+    p = new (p) B [2][2][MAX / 8 - 2];
+    p = new (p) B [2][2][MAX / 8 - 3];
+
+    p = new (p) B [2][MAX][2];          // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 1][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 2][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX - 99][2];     // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 1][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 2][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 3][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 4][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 5][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 6][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 7][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 2 - 8][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 4][2];      // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 4 - 1][2];  // { dg-error "size of array" }
+    p = new (p) B [2][MAX / 4 - 2][2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [2][MAX / 8][2];
+    // p = new (p) B [2][MAX / 8 - 1][2];
+
+    p = new (p) B [2][MAX / 8 - 2][2];
+    p = new (p) B [2][MAX / 8 - 3][2];
+
+    p = new (p) B [MAX][2][2];          // { dg-error "size of array" }
+    p = new (p) B [MAX - 1][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX - 2][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX - 99][2][2];     // { dg-error "size of array" }
+    p = new (p) B [MAX / 2][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 1][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 2][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 3][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 4][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 5][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 6][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 7][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 2 - 8][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 4][2][2];      // { dg-error "size of array" }
+    p = new (p) B [MAX / 4 - 1][2][2];  // { dg-error "size of array" }
+    p = new (p) B [MAX / 4 - 2][2][2];  // { dg-error "size of array" }
+
+    // Avoid exercising data model-dependent expressions.
+    // p = new (p) B [MAX / 8][2][2];
+    // p = new (p) B [MAX / 8 - 1][2][2];
+
+    p = new (p) B [MAX / 8 - 2][2][2];
+    p = new (p) B [MAX / 8 - 3][2][2];
+}
diff --git a/gcc/testsuite/g++.dg/init/new45.C b/gcc/testsuite/g++.dg/init/new45.C
new file mode 100644
index 0000000..92dac18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new45.C
@@ -0,0 +1,106 @@
+// { dg-do compile }
+// { dg-options "-O1" }
+// { dg-final { scan-assembler-not "abort" } }
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C" {
+    void abort ();
+    void* malloc (size_t);
+}
+
+struct UDClass {
+    static int n;
+    UDClass () { ++n; }
+    virtual ~UDClass () { --n; }
+};
+
+int UDClass::n;
+
+struct POD {
+    char buf [sizeof (UDClass)];
+};
+
+enum { N = 123 };
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n)
+{
+    // Verify that array new is invoked with an argument large enough
+    // for the array and a size_t cookie to store the number of elements.
+    // (This holds for classes with user-defined types but not POD types).
+    if (n != N * sizeof (UDClass) + sizeof n) abort ();
+    return malloc (n);
+}
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n, void *p)
+{
+    // Verify that the default placement array new is invoked with
+    // an argument just large enough for the array (and no cookie),
+    // regardless of whether the type is a POD or class with a user
+    // defined ctor.
+    if (n != N * sizeof (UDClass)) abort ();
+    return p;
+}
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n, POD *p)
+{
+    // Verify that placement array new overload for a POD type is
+    // invoked with an argument large enough for the array and
+    // a cookie.
+    if (n != N * sizeof (POD)) abort ();
+    return p;
+}
+
+inline __attribute__ ((always_inline))
+void* operator new[] (size_t n, UDClass *p)
+{
+    // Verify that placement array new overload for a class type with
+    // a user-defined ctor and dtor is invoked with an argument large
+    // enough for the array and a cookie.
+    if (n != N * sizeof (UDClass) + sizeof n) abort ();
+    return p;
+}
+
+// UDClassllocate a sufficiently large buffer to construct arrays into.
+static unsigned char buf [N * N];
+
+POD* test_new_POD ()
+{
+    // Avoid testing PODs since for those, the global new is invoked
+    // without the overhead of a cookie.
+    // return new POD [N];
+    return 0;
+}
+
+POD* test_default_placement_new_POD ()
+{
+    // Vefify that no overhead is allocated.
+    return new (buf) POD [N];
+}
+
+POD* test_overloaded_placement_new_POD ()
+{
+    // Vefify that no overhead is allocated.
+    return new ((POD*)buf) POD [N];
+}
+
+UDClass* test_new_UDClass ()
+{
+    // Vefify that space for a cookie is allocated.
+    return new UDClass [N];
+}
+
+UDClass* test_default_placement_new_UDClass ()
+{
+    // Vefify that no overhead is allocated.
+    return new (buf) UDClass [N];
+}
+
+UDClass* test_overloaded_placement_new_UDClass ()
+{
+    // Vefify that space for a cookie is allocated.
+    return new ((UDClass*)buf) UDClass [N];
+}
diff --git a/gcc/testsuite/g++.dg/other/new-size-type.C b/gcc/testsuite/g++.dg/other/new-size-type.C
index 04933fd..ad4dc10 100644
--- a/gcc/testsuite/g++.dg/other/new-size-type.C
+++ b/gcc/testsuite/g++.dg/other/new-size-type.C
@@ -5,6 +5,5 @@
 const char*
 foo()
 {
-    return new char[~static_cast<size_t>(0)];// { dg-bogus "large" }
+    return new char[~static_cast<size_t>(0)];// { dg-error "size of array" }
 }
-

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-11-05 19:30     ` Martin Sebor
@ 2015-11-06 14:21       ` Jason Merrill
  2015-11-26 10:23       ` James Greenhalgh
  1 sibling, 0 replies; 9+ messages in thread
From: Jason Merrill @ 2015-11-06 14:21 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

OK.

Jason

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-11-05 19:30     ` Martin Sebor
  2015-11-06 14:21       ` Jason Merrill
@ 2015-11-26 10:23       ` James Greenhalgh
  2015-11-26 11:33         ` Ramana Radhakrishnan
  1 sibling, 1 reply; 9+ messages in thread
From: James Greenhalgh @ 2015-11-26 10:23 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, Gcc Patch List

On Thu, Nov 05, 2015 at 12:30:08PM -0700, Martin Sebor wrote:
> On 11/02/2015 09:55 PM, Jason Merrill wrote:
> >On 10/26/2015 10:06 PM, Martin Sebor wrote:
> >>+      if (TREE_CONSTANT (maybe_constant_value (outer_nelts)))
> >>+    {
> >>+      if (tree_int_cst_lt (max_outer_nelts_tree, outer_nelts))
> >
> >maybe_constant_value may return a constant, but that doesn't mean that
> >outer_nelts was already constant; if it wasn't, the call to
> >tree_int_cst_lt will fail.
> 
> Thanks for the hint. I wasn't able to trigger the failure. I suspect
> outer_nelts must have already been folded at this point because the
> maybe_constant_value call isn't necessary. I removed it.
> 
> >Since we're moving toward delayed folding, I'd prefer to use the result
> >of maybe_constant_value only for this diagnostic, and then continue to
> >pass the unfolded value along.
> 
> Sure. Done in the attached patch.
> 
> Martin

> gcc/cp/ChangeLog
> 
> 2015-10-19  Martin Sebor  <msebor@redhat.com>
> 
> 	PR c++/67913
> 	PR c++/67927
> 	* call.c (build_operator_new_call): Do not assume size_check
> 	is non-null, analogously to the top half of the function.
> 	* init.c (build_new_1): Detect and diagnose array sizes in
> 	excess of the maximum of roughly SIZE_MAX / 2.
> 	Insert a runtime check only for arrays with a non-constant size.
> 	(build_new): Detect and diagnose negative array sizes.
> 
> gcc/testsuite/ChangeLog
> 
> 2015-10-19  Martin Sebor  <msebor@redhat.com>
> 
> 	* init/new45.C: New test to verify that operator new is invoked
> 	with or without overhead for a cookie.

This new test fails for ARM targets, snipping some of the diff...

> +inline __attribute__ ((always_inline))
> +void* operator new[] (size_t n)
> +{
> +    // Verify that array new is invoked with an argument large enough
> +    // for the array and a size_t cookie to store the number of elements.
> +    // (This holds for classes with user-defined types but not POD types).
> +    if (n != N * sizeof (UDClass) + sizeof n) abort ();
> +    return malloc (n);
> +}

Cookies on ARM are 8-bytes [1], but sizeof ((size_t) n) is only 4-bytes,
so this check will fail (We'll ask for 500 bytes, the test here will only
be looking for 496).

Would it undermine the test for other architectures if I were to swap out
the != for a >= ? I think that is in line with the "argument large enough
for the array" that this test is looking for, but would not catch bugs where
we were allocating more memory than neccessary.

Otherwise I can spin a patch which skips the test for ARM targets.

Thanks,
James

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-11-26 10:23       ` James Greenhalgh
@ 2015-11-26 11:33         ` Ramana Radhakrishnan
  2015-11-26 17:55           ` Martin Sebor
  0 siblings, 1 reply; 9+ messages in thread
From: Ramana Radhakrishnan @ 2015-11-26 11:33 UTC (permalink / raw)
  To: James Greenhalgh, Martin Sebor; +Cc: Jason Merrill, Gcc Patch List

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


> Cookies on ARM are 8-bytes [1], but sizeof ((size_t) n) is only 4-bytes,
> so this check will fail (We'll ask for 500 bytes, the test here will only
> be looking for 496).
> 
> Would it undermine the test for other architectures if I were to swap out
> the != for a >= ? I think that is in line with the "argument large enough
> for the array" that this test is looking for, but would not catch bugs where
> we were allocating more memory than neccessary.
> 
> Otherwise I can spin a patch which skips the test for ARM targets.
> 

I didn't want to skip this for ARM, instead something that takes into account the cookie size - (very gratuitous hack was to just add 4 in a #ifdef __arm__ block). Something like attached, brown paper bag warning ;)
 

* g++.dg/init/new45.C: Adjust for cookie size on arm.

regards
Ramana

> Thanks,
> James
> 

[-- Attachment #2: new45-fixup.txt --]
[-- Type: text/plain, Size: 1651 bytes --]

diff --git a/gcc/testsuite/g++.dg/init/new45.C b/gcc/testsuite/g++.dg/init/new45.C
index 92dac18..31473a3 100644
--- a/gcc/testsuite/g++.dg/init/new45.C
+++ b/gcc/testsuite/g++.dg/init/new45.C
@@ -29,8 +29,16 @@ void* operator new[] (size_t n)
     // Verify that array new is invoked with an argument large enough
     // for the array and a size_t cookie to store the number of elements.
     // (This holds for classes with user-defined types but not POD types).
-    if (n != N * sizeof (UDClass) + sizeof n) abort ();
-    return malloc (n);
+  size_t val = N * sizeof (UDClass) + sizeof n;
+
+    // On ARM EABI the cookie is always 8 bytes as per Section 3.2.2
+    // of http://infocenter.arm.com/help/topic/com.arm.doc.ihi0041d/IHI0041D_cppabi.pdf
+#if defined( __arm__) && defined(__ARM_EABI__)
+  val = val + 4;
+#endif
+
+  if (n != val) abort ();
+  return malloc (n);
 }
 
 inline __attribute__ ((always_inline))
@@ -60,8 +68,16 @@ void* operator new[] (size_t n, UDClass *p)
     // Verify that placement array new overload for a class type with
     // a user-defined ctor and dtor is invoked with an argument large
     // enough for the array and a cookie.
-    if (n != N * sizeof (UDClass) + sizeof n) abort ();
-    return p;
+  size_t val = N * sizeof (UDClass) + sizeof n;
+
+    // On ARM EABI the cookie is always 8 bytes as per Section 3.2.2
+    // of http://infocenter.arm.com/help/topic/com.arm.doc.ihi0041d/IHI0041D_cppabi.pdf
+#if defined( __arm__) && defined(__ARM_EABI__)
+  val = val + 4;
+#endif
+
+  if (n != val) abort ();
+  return p;
 }
 
 // UDClassllocate a sufficiently large buffer to construct arrays into.

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-11-26 11:33         ` Ramana Radhakrishnan
@ 2015-11-26 17:55           ` Martin Sebor
  2015-11-27  3:54             ` Martin Sebor
  0 siblings, 1 reply; 9+ messages in thread
From: Martin Sebor @ 2015-11-26 17:55 UTC (permalink / raw)
  To: Ramana Radhakrishnan, James Greenhalgh; +Cc: Jason Merrill, Gcc Patch List

On 11/26/2015 04:33 AM, Ramana Radhakrishnan wrote:
>
>> Cookies on ARM are 8-bytes [1], but sizeof ((size_t) n) is only 4-bytes,
>> so this check will fail (We'll ask for 500 bytes, the test here will only
>> be looking for 496).
>>
>> Would it undermine the test for other architectures if I were to swap out
>> the != for a >= ? I think that is in line with the "argument large enough
>> for the array" that this test is looking for, but would not catch bugs where
>> we were allocating more memory than neccessary.
>>
>> Otherwise I can spin a patch which skips the test for ARM targets.
>>
>
> I didn't want to skip this for ARM, instead something that takes into account the cookie size - (very gratuitous hack was to just add 4 in a #ifdef __arm__ block). Something like attached, brown paper bag warning ;)

Thanks. I'll commit it today after some testing.

I should probably also check to see if there are other such targets
and try to find a way to generalize the test. (There should be a way
to expose the cookie size to programs, otherwise they have no way to
avoid buffer overflow in array forms of placement new).

Martin

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

* Re: [PING] Re: [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements
  2015-11-26 17:55           ` Martin Sebor
@ 2015-11-27  3:54             ` Martin Sebor
  0 siblings, 0 replies; 9+ messages in thread
From: Martin Sebor @ 2015-11-27  3:54 UTC (permalink / raw)
  To: Ramana Radhakrishnan, James Greenhalgh; +Cc: Jason Merrill, Gcc Patch List

On 11/26/2015 10:45 AM, Martin Sebor wrote:
> On 11/26/2015 04:33 AM, Ramana Radhakrishnan wrote:
>>
>>> Cookies on ARM are 8-bytes [1], but sizeof ((size_t) n) is only 4-bytes,
>>> so this check will fail (We'll ask for 500 bytes, the test here will
>>> only
>>> be looking for 496).
>>>
>>> Would it undermine the test for other architectures if I were to swap
>>> out
>>> the != for a >= ? I think that is in line with the "argument large
>>> enough
>>> for the array" that this test is looking for, but would not catch
>>> bugs where
>>> we were allocating more memory than neccessary.
>>>
>>> Otherwise I can spin a patch which skips the test for ARM targets.
>>>
>>
>> I didn't want to skip this for ARM, instead something that takes into
>> account the cookie size - (very gratuitous hack was to just add 4 in a
>> #ifdef __arm__ block). Something like attached, brown paper bag
>> warning ;)
>
> Thanks. I'll commit it today after some testing.

I've checked in a slightly modified version of your patch in r230987.

>
> I should probably also check to see if there are other such targets
> and try to find a way to generalize the test. (There should be a way
> to expose the cookie size to programs, otherwise they have no way to
> avoid buffer overflow in array forms of placement new).

There don't appear to be other targets besides ARM that override
the default cookie size.  I opened enhancement c++/68571 - provide
__builtin_cookie_size, to provide an API to make it possible for
programs to query this constant.

Martin

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

end of thread, other threads:[~2015-11-27  0:35 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-19 23:06 [PATCH] c++/67913, 67917 - fix new expression with wrong number of elements Martin Sebor
2015-10-27  2:47 ` [PING] " Martin Sebor
2015-11-03  4:55   ` Jason Merrill
2015-11-05 19:30     ` Martin Sebor
2015-11-06 14:21       ` Jason Merrill
2015-11-26 10:23       ` James Greenhalgh
2015-11-26 11:33         ` Ramana Radhakrishnan
2015-11-26 17:55           ` Martin Sebor
2015-11-27  3:54             ` 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).