diff mbox series

handle invalid array index in warn_placement_new_too_small (PR 83058)

Message ID 42cfde95-0480-8781-0413-23f2de315735@gmail.com
State New
Headers show
Series handle invalid array index in warn_placement_new_too_small (PR 83058) | expand

Commit Message

Martin Sebor Nov. 27, 2017, 7:24 p.m. UTC
Bug 83058 - [6/7/8 Regression] ICE on C++ code with negative array
index: in warn_placement_new_too_small, at cp/init.c:2666, reports
an ICE for an invalid placement new expression with a negative
array index.

The ICE was caused by calling tree_to_uhwi (nelts) without first
checking to make sure nelts is fits in an unsigned HOST_WIDE_INT.

While testing the code for similar boundary conditions I also found
a few other cases that the code doesn't handle entirely correctly.

To avoid the ICE the attached patch uses offset_int instead of
HOST_WIDE_INT to track offsets and sizes.  It also improves
the handling of the other edge cases.

Martin

Comments

Jason Merrill Nov. 27, 2017, 10:11 p.m. UTC | #1
On 11/27/2017 02:24 PM, Martin Sebor wrote:
> Bug 83058 - [6/7/8 Regression] ICE on C++ code with negative array
> index: in warn_placement_new_too_small, at cp/init.c:2666, reports
> an ICE for an invalid placement new expression with a negative
> array index.
> 
> The ICE was caused by calling tree_to_uhwi (nelts) without first
> checking to make sure nelts is fits in an unsigned HOST_WIDE_INT.
> 
> While testing the code for similar boundary conditions I also found
> a few other cases that the code doesn't handle entirely correctly.
> 
> To avoid the ICE the attached patch uses offset_int instead of
> HOST_WIDE_INT to track offsets and sizes.  It also improves
> the handling of the other edge cases.

OK.

Jason
diff mbox series

Patch

PR c++/83058 - [6/7/8 Regression] ICE on C++ code with negative array index: in warn_placement_new_too_small

gcc/cp/ChangeLog:

	PR c++/83058
	* init.c (warn_placement_new_too_small): Use offset_int instead of
	HOST_WIDE_INT.

gcc/testsuite/ChangeLog:

	PR c++/83058
	* g++.dg/warn/Wplacement-new-size-5.C: New test.

Index: gcc/cp/init.c
===================================================================
--- gcc/cp/init.c	(revision 255173)
+++ gcc/cp/init.c	(working copy)
@@ -2498,9 +2498,9 @@  warn_placement_new_too_small (tree type, tree nelt
 
   /* The number of bytes to add to or subtract from the size of the provided
      buffer based on an offset into an array or an array element reference.
-     Although intermediate results may be negative (as in a[3] - 2) the final
-     result cannot be.  */
-  HOST_WIDE_INT adjust = 0;
+     Although intermediate results may be negative (as in a[3] - 2) a valid
+     final result cannot be.  */
+  offset_int adjust = 0;
   /* True when the size of the entire destination object should be used
      to compute the possibly optimistic estimate of the available space.  */
   bool use_obj_size = false;
@@ -2524,7 +2524,7 @@  warn_placement_new_too_small (tree type, tree nelt
      is a constant.  */
   if (TREE_CODE (oper) == POINTER_PLUS_EXPR)
     {
-      /* If the offset is comple-time constant, use it to compute a more
+      /* If the offset is compile-time constant, use it to compute a more
 	 accurate estimate of the size of the buffer.  Since the operand
 	 of POINTER_PLUS_EXPR is represented as an unsigned type, convert
 	 it to signed first.
@@ -2532,7 +2532,7 @@  warn_placement_new_too_small (tree type, tree nelt
 	 estimate (this may lead to false negatives).  */
       tree adj = TREE_OPERAND (oper, 1);
       if (CONSTANT_CLASS_P (adj))
-	adjust += tree_to_shwi (convert (ssizetype, adj));
+	adjust += wi::to_offset (convert (ssizetype, adj));
       else
 	use_obj_size = true;
 
@@ -2559,9 +2559,9 @@  warn_placement_new_too_small (tree type, tree nelt
 	 not a compile-time constant, use the index to determine the
 	 size of the buffer.  Otherwise, use the entire array as
 	 an optimistic estimate of the size.  */
-      const_tree adj = TREE_OPERAND (oper, 1);
+      const_tree adj = fold_non_dependent_expr (TREE_OPERAND (oper, 1));
       if (!use_obj_size && CONSTANT_CLASS_P (adj))
-	adjust += tree_to_shwi (adj);
+	adjust += wi::to_offset (adj);
       else
 	{
 	  use_obj_size = true;
@@ -2580,10 +2580,18 @@  warn_placement_new_too_small (tree type, tree nelt
      members from arrays of unspecified size.  */
   bool compref = TREE_CODE (oper) == COMPONENT_REF;
 
+  /* For COMPONENT_REF (i.e., a struct member) the size of the entire
+     enclosing struct.  Used to validate the adjustment (offset) into
+     an array at the end of a struct.  */
+  offset_int compsize = 0;
+
   /* Descend into a struct or union to find the member whose address
      is being used as the argument.  */
   if (TREE_CODE (oper) == COMPONENT_REF)
     {
+      tree comptype = TREE_TYPE (TREE_OPERAND (oper, 0));
+      compsize = wi::to_offset (TYPE_SIZE_UNIT (comptype));
+
       tree op0 = oper;
       while (TREE_CODE (op0 = TREE_OPERAND (op0, 0)) == COMPONENT_REF);
       if (VAR_P (op0))
@@ -2591,7 +2599,8 @@  warn_placement_new_too_small (tree type, tree nelt
       oper = TREE_OPERAND (oper, 1);
     }
 
-  if ((addr_expr || !POINTER_TYPE_P (TREE_TYPE (oper)))
+  tree opertype = TREE_TYPE (oper);
+  if ((addr_expr || !POINTER_TYPE_P (opertype))
       && (VAR_P (oper)
 	  || TREE_CODE (oper) == FIELD_DECL
 	  || TREE_CODE (oper) == PARM_DECL))
@@ -2598,7 +2607,7 @@  warn_placement_new_too_small (tree type, tree nelt
     {
       /* A possibly optimistic estimate of the number of bytes available
 	 in the destination buffer.  */
-      unsigned HOST_WIDE_INT bytes_avail = 0;
+      offset_int bytes_avail = 0;
       /* True when the estimate above is in fact the exact size
 	 of the destination buffer rather than an estimate.  */
       bool exact_size = true;
@@ -2613,47 +2622,42 @@  warn_placement_new_too_small (tree type, tree nelt
 	  /* Use the size of the entire array object when the expression
 	     refers to a variable or its size depends on an expression
 	     that's not a compile-time constant.  */
-	  bytes_avail = tree_to_uhwi (DECL_SIZE_UNIT (oper));
+	  bytes_avail = wi::to_offset (DECL_SIZE_UNIT (oper));
 	  exact_size = !use_obj_size;
 	}
-      else if (TYPE_SIZE_UNIT (TREE_TYPE (oper))
-	       && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (oper))))
+      else if (tree opersize = TYPE_SIZE_UNIT (opertype))
 	{
 	  /* Use the size of the type of the destination buffer object
-	     as the optimistic estimate of the available space in it.  */
-	  bytes_avail = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (oper)));
+	     as the optimistic estimate of the available space in it.
+	     Use the maximum possible size for zero-size arrays and
+	     flexible array members (except of initialized objects
+	     thereof).  */
+	  if (TREE_CODE (opersize) == INTEGER_CST)
+	    bytes_avail = wi::to_offset (opersize);
 	}
-      else if (var_decl)
-	{
-	  /* Constructing into a buffer provided by the flexible array
-	     member of a declared object (which is permitted as a G++
-	     extension).  If the array member has been initialized,
-	     determine its size from the initializer.  Otherwise,
-	     the array size is zero.  */
-	  bytes_avail = 0;
 
-	  if (tree init = find_field_init (oper, DECL_INITIAL (var_decl)))
-	    bytes_avail = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (init)));
-	}
-      else
+      if (bytes_avail == 0)
 	{
-	  /* Bail if neither the size of the object nor its type is known.  */
-	  return;
+	  if (var_decl)
+	    {
+	      /* Constructing into a buffer provided by the flexible array
+		 member of a declared object (which is permitted as a G++
+		 extension).  If the array member has been initialized,
+		 determine its size from the initializer.  Otherwise,
+		 the array size is zero.  */
+	      if (tree init = find_field_init (oper, DECL_INITIAL (var_decl)))
+		bytes_avail = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (init)));
+	    }
+	  else
+	    bytes_avail = (wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node))
+			   - compsize);
 	}
 
-      tree_code oper_code = TREE_CODE (TREE_TYPE (oper));
+      tree_code oper_code = TREE_CODE (opertype);
 
       if (compref && oper_code == ARRAY_TYPE)
 	{
-	  /* Avoid diagnosing flexible array members (which are accepted
-	     as an extension and diagnosed with -Wpedantic) and zero-length
-	     arrays (also an extension).
-	     Overflowing construction in one-element arrays is diagnosed
-	     only at level 2.  */
-	  if (bytes_avail == 0 && !var_decl)
-	    return;
-
-	  tree nelts = array_type_nelts_top (TREE_TYPE (oper));
+	  tree nelts = array_type_nelts_top (opertype);
 	  tree nelts_cst = maybe_constant_value (nelts);
 	  if (TREE_CODE (nelts_cst) == INTEGER_CST
 	      && integer_onep (nelts_cst)
@@ -2662,29 +2666,35 @@  warn_placement_new_too_small (tree type, tree nelt
 	    return;
 	}
 
-      /* The size of the buffer can only be adjusted down but not up.  */
-      gcc_checking_assert (0 <= adjust);
-
       /* Reduce the size of the buffer by the adjustment computed above
 	 from the offset and/or the index into the array.  */
-      if (bytes_avail < static_cast<unsigned HOST_WIDE_INT>(adjust))
+      if (bytes_avail < adjust || adjust < 0)
 	bytes_avail = 0;
       else
-	bytes_avail -= adjust;
+	{
+	  tree elttype = (TREE_CODE (opertype) == ARRAY_TYPE
+			  ? TREE_TYPE (opertype) : opertype);
+	  if (tree eltsize = TYPE_SIZE_UNIT (elttype))
+	    {
+	      bytes_avail -= adjust * wi::to_offset (eltsize);
+	      if (bytes_avail < 0)
+		bytes_avail = 0;
+	    }
+	}
 
       /* The minimum amount of space needed for the allocation.  This
 	 is an optimistic estimate that makes it possible to detect
 	 placement new invocation for some undersize buffers but not
 	 others.  */
-      unsigned HOST_WIDE_INT bytes_need;
+      offset_int bytes_need;
 
       if (CONSTANT_CLASS_P (size))
-	bytes_need = tree_to_uhwi (size);
+	bytes_need = wi::to_offset (size);
       else if (nelts && CONSTANT_CLASS_P (nelts))
-	  bytes_need = tree_to_uhwi (nelts)
-	    * tree_to_uhwi (TYPE_SIZE_UNIT (type));
+	bytes_need = (wi::to_offset (nelts)
+		      * wi::to_offset (TYPE_SIZE_UNIT (type)));
       else if (tree_fits_uhwi_p (TYPE_SIZE_UNIT (type)))
-	bytes_need = tree_to_uhwi (TYPE_SIZE_UNIT (type));
+	bytes_need = wi::to_offset (TYPE_SIZE_UNIT (type));
       else
 	{
 	  /* The type is a VLA.  */
@@ -2703,9 +2713,8 @@  warn_placement_new_too_small (tree type, tree nelt
 			  : "placement new constructing an object of type "
 			  "%<%T [%wu]%> and size %qwu in a region of type %qT "
 			  "and size at most %qwu",
-			  type, tree_to_uhwi (nelts), bytes_need,
-			  TREE_TYPE (oper),
-			  bytes_avail);
+			  type, tree_to_uhwi (nelts), bytes_need.to_uhwi (),
+			  opertype, bytes_avail.to_uhwi ());
 	    else
 	      warning_at (loc, OPT_Wplacement_new_,
 			  exact_size ?
@@ -2715,8 +2724,8 @@  warn_placement_new_too_small (tree type, tree nelt
 			  : "placement new constructing an array of objects "
 			  "of type %qT and size %qwu in a region of type %qT "
 			  "and size at most %qwu",
-			  type, bytes_need, TREE_TYPE (oper),
-			  bytes_avail);
+			  type, bytes_need.to_uhwi (), opertype,
+			  bytes_avail.to_uhwi ());
 	  else
 	    warning_at (loc, OPT_Wplacement_new_,
 			exact_size ?
@@ -2725,8 +2734,8 @@  warn_placement_new_too_small (tree type, tree nelt
 			: "placement new constructing an object of type %qT "
 			"and size %qwu in a region of type %qT and size "
 			"at most %qwu",
-			type, bytes_need, TREE_TYPE (oper),
-			bytes_avail);
+			type, bytes_need.to_uhwi (), opertype,
+			bytes_avail.to_uhwi ());
 	}
     }
 }
Index: gcc/testsuite/g++.dg/warn/Wplacement-new-size-5.C
===================================================================
--- gcc/testsuite/g++.dg/warn/Wplacement-new-size-5.C	(nonexistent)
+++ gcc/testsuite/g++.dg/warn/Wplacement-new-size-5.C	(working copy)
@@ -0,0 +1,241 @@ 
+// PR c++/83058 - ICE on C++ code with negative array index: in
+// warn_placement_new_too_small
+// { dg-do compile }
+// { dg-additional-options "-Wplacement-new -Wno-pedantic" }
+
+#define SIZE_MAX   __SIZE_MAX__
+#define DIFF_MAX   __PTRDIFF_MAX__
+#define DIFF_MIN   (-DIFF_MAX - 1)
+
+void* operator new (__SIZE_TYPE__ n, void *p) { return p; }
+void* operator new[] (__SIZE_TYPE__ n, void *p) { return p; }
+
+struct A { };
+
+char carr[2];
+int iarr[2];
+
+struct C0 { char i, carr[0]; };
+struct I0 { int i, iarr[0]; };
+struct CX { char i, carr[]; };
+struct IX { int i, iarr[]; };
+
+void test_single (C0 *pc, CX *qc, I0 *pi, IX *qi, int n)
+{
+  new (&carr[DIFF_MIN]) A ();       // { dg-warning "placement new constructing an object of type .A. and size .1. in a region of type .char \\\[2]. and size .0." }
+  new (&carr[-1]) A;                // { dg-warning "\\\[-Wplacement-new" }
+  new (carr -1 ) A;                 // { dg-warning "\\\[-Wplacement-new" }
+  new (&carr[0]) A;
+  new (carr) A;
+  new (&carr[1]) A;
+  new (carr + 1) A;
+  new (&carr[n]) A;
+  new (carr + n) A;
+  new (&carr[DIFF_MAX]) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (carr + DIFF_MAX) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (&carr[SIZE_MAX]) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (carr + SIZE_MAX) A;          // { dg-warning "\\\[-Wplacement-new" }
+
+  new (&pc->carr[DIFF_MIN]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[-1]) A;            // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[0]) A;
+  new (&pc->carr[9]) A;
+  new (&pc->carr[n]) A;
+  new (&pc->carr[DIFF_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[SIZE_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    /* The highest index at which a single A can be constructed.  */
+    enum { MAX = DIFF_MAX - sizeof *pc - sizeof (A) };
+    new (&pc->carr[MAX]) A;
+    new (&pc->carr[MAX + 1]) A;     // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&qc->carr[DIFF_MIN]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[-1]) A;            // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[0]) A;
+  new (&qc->carr[9]) A;
+  new (&qc->carr[n]) A;
+  new (&qc->carr[DIFF_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[SIZE_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    /* The highest index at which a single A can be constructed.  */
+    enum { MAX = DIFF_MAX - sizeof *qc - sizeof (A) };
+    new (&qc->carr[MAX]) A;
+    new (&qc->carr[MAX + 1]) A;     // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&pi->iarr[DIFF_MIN]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[-1]) A;            // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[0]) A;
+  new (&pi->iarr[9]) A;
+  new (&pi->iarr[n]) A;
+  new (&pi->iarr[DIFF_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[SIZE_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = (DIFF_MAX - sizeof *pi) / sizeof *pi->iarr };
+    new (&pi->iarr[MAX]) A;
+    new (&pi->iarr[MAX + 1]) A;     // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&qi->iarr[DIFF_MIN]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[-1]) A;            // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[0]) A;
+  new (&qi->iarr[9]) A;
+  new (&qi->iarr[n]) A;
+  new (&qi->iarr[DIFF_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[SIZE_MAX]) A;      // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = (DIFF_MAX - sizeof *qi) / sizeof *qi->iarr };
+    new (&qi->iarr[MAX]) A;
+    new (&qi->iarr[MAX + 1]) A;     // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&iarr[DIFF_MIN]) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[-1]) A;                // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[1]) A;
+  new (&iarr[n]) A;
+  new (&iarr[DIFF_MAX]) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[SIZE_MAX]) A;          // { dg-warning "\\\[-Wplacement-new" }
+}
+
+void test_array_1 (C0 *pc, CX *qc, I0 *pi, IX *qi)
+{
+  enum { N = 1 };
+
+  new (&carr[DIFF_MIN]) A[N];       // { dg-warning "placement new constructing an object of type .A \\\[\[0-9\]+]. and size .\[0-9\]+. in a region of type .char \\\[2]. and size .0." }
+  new (&carr[-1]) A[N];             // { dg-warning "\\\[-Wplacement-new" }
+  new (&carr[DIFF_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+  new (&carr[SIZE_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+
+  new (&pc->carr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = DIFF_MAX - sizeof *pc - sizeof (A[N]) };
+    new (&pc->carr[MAX]) A[N];
+    new (&pc->carr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&qc->carr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = DIFF_MAX - sizeof *qc - sizeof (A[N]) };
+    new (&qc->carr[MAX]) A[N];
+    new (&qc->carr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&pi->iarr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = (DIFF_MAX - sizeof *pi) / sizeof *pi->iarr };
+    new (&pi->iarr[MAX]) A[N];
+    new (&pi->iarr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&qi->iarr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = (DIFF_MAX - sizeof *qi) / sizeof *qi->iarr };
+    new (&qi->iarr[MAX]) A[N];
+    new (&qi->iarr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&iarr[DIFF_MIN]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[-1]) A[N];             // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[DIFF_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[SIZE_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+}
+
+
+void test_array_3 (C0 *pc, CX *qc, I0 *pi, IX *qi)
+{
+  enum { N = 3 };
+
+  new (&carr[DIFF_MIN]) A[N];       // { dg-warning "placement new constructing an object of type .A \\\[\[0-9\]+]. and size .\[0-9\]+. in a region of type .char \\\[2]. and size .0." }
+  new (&carr[-1]) A[N];             // { dg-warning "\\\[-Wplacement-new" }
+  new (&carr[DIFF_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+  new (&carr[SIZE_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+
+  new (&pc->carr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pc->carr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = DIFF_MAX - sizeof *pc - sizeof (A[N]) };
+    new (&pc->carr[MAX]) A[N];
+    new (&pc->carr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&qc->carr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qc->carr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = DIFF_MAX - sizeof *qc - sizeof (A[N]) };
+    new (&qc->carr[MAX]) A[N];
+    new (&qc->carr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&pi->iarr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&pi->iarr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = (DIFF_MAX - sizeof *pi) / sizeof *pi->iarr };
+    new (&pi->iarr[MAX]) A[N];
+    new (&pi->iarr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&qi->iarr[DIFF_MIN]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[-1]) A[N];         // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[DIFF_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+  new (&qi->iarr[SIZE_MAX]) A[N];   // { dg-warning "\\\[-Wplacement-new" }
+
+  {
+    enum { MAX = (DIFF_MAX - sizeof *qi) / sizeof *qi->iarr };
+    new (&qi->iarr[MAX]) A[N];
+    new (&qi->iarr[MAX + 1]) A[N];  // { dg-warning "\\\[-Wplacement-new" }
+  }
+
+  new (&iarr[DIFF_MIN]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[-1]) A[N];             // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[DIFF_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+  new (&iarr[SIZE_MAX]) A[N];       // { dg-warning "\\\[-Wplacement-new" }
+}
+
+
+void test_vla (unsigned n)
+{
+  char cvla[n];
+
+  new (&cvla[DIFF_MIN]) A;          // { dg-warning "placement new constructing an object of type .A. and size .1. in a region of type .char \\\[n]. and size .0." }
+  new (&cvla[-1]) A;                // { dg-warning "\\\[-Wplacement-new" }
+  new (cvla -1) A;                  // { dg-warning "\\\[-Wplacement-new" }
+  new (&cvla[0]) A;
+  new (&cvla[9]) A;
+  new (&cvla[n - 1]) A;
+  new (cvla + n - 1) A;
+  new (&cvla[DIFF_MAX - 1]) A;
+  new (&cvla[DIFF_MAX]) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (cvla + DIFF_MAX) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (&cvla[SIZE_MAX]) A;          // { dg-warning "\\\[-Wplacement-new" }
+  new (cvla + SIZE_MAX) A;          // { dg-warning "\\\[-Wplacement-new" }
+}