PR c++/67942 - diagnose placement new buffer overflow
authorMartin Sebor <msebor@redhat.com>
Thu, 5 Nov 2015 21:42:10 +0000 (21:42 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Thu, 5 Nov 2015 21:42:10 +0000 (14:42 -0700)
gcc/
        * invoke.texi (-Wplacement-new): Document new option.
* gcc/testsuite/g++.dg/warn/Wplacement-new-size.C: New test.

gcc/c-family/
        * c.opt (-Wplacement-new): New option.

gcc/cp/
* cp/init.c (warn_placement_new_too_small): New function.
(build_new_1): Call it.

gcc/testsuite/
        * g++.dg/warn/Wplacement-new-size.C: New test.

From-SVN: r229827

gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/init.c
gcc/doc/invoke.texi
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/warn/Wplacement-new-size.C [new file with mode: 0644]

index 0dd763afc208136e623ed38202646a1c60f8dcd2..94e597e025a0c1592898e33921ffb61c4829f598 100644 (file)
@@ -1,3 +1,9 @@
+2015-11-05  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/67942
+       * invoke.texi (-Wplacement-new): Document new option.
+       * gcc/testsuite/g++.dg/warn/Wplacement-new-size.C: New test.
+
 2015-11-05  Alan Lawrence  <alan.lawrence@arm.com>
 
        PR tree-optimization/65963
index ffb97c94dfd38ab49c8aa22e690e4ae213da68d7..c21f50410faeb0a79b2382fed3ac3d56cb39c9e2 100644 (file)
@@ -1,3 +1,8 @@
+2015-11-05  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/67942
+       * c.opt (-Wplacement-new): New option.
+
 2015-11-05  Jakub Jelinek  <jakub@redhat.com>
 
        * c-common.h (c_finish_omp_atomic): Add TEST argument.
index e573254686eb0be6688bce63b8b22dcd9ef8882e..5f38018d35e917b1664c7f6f6b531f0860be0332 100644 (file)
@@ -776,6 +776,10 @@ Wprotocol
 ObjC ObjC++ Var(warn_protocol) Init(1) Warning
 Warn if inherited methods are unimplemented.
 
+Wplacement-new
+C++ Var(warn_placement_new) Init(1) Warning
+Warn for placement new expressions with undefined behavior
+
 Wredundant-decls
 C ObjC C++ ObjC++ Var(warn_redundant_decls) Warning
 Warn about multiple declarations of the same object.
index 57de3e6b639608ef8dbd328474442167a2cdb276..b84778f32428edcd5d493e9c3e9f951c86d02d9d 100644 (file)
@@ -1,3 +1,9 @@
+2015-11-05  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/67942
+       * cp/init.c (warn_placement_new_too_small): New function.
+       (build_new_1): Call it.
+
 2015-11-05  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/67846
index 035ec2921be8371ee60009375cafe97be2ef8cdc..7600363447d1f50acc60021425d61e89571272b1 100644 (file)
@@ -2276,6 +2276,201 @@ throw_bad_array_new_length (void)
   return build_cxx_call (fn, 0, NULL, tf_warning_or_error);
 }
 
+/* Attempt to verify that the argument, OPER, of a placement new expression
+   refers to an object sufficiently large for an object of TYPE or an array
+   of NELTS of such objects when NELTS is non-null, and issue a warning when
+   it does not.  SIZE specifies the size needed to construct the object or
+   array and captures the result of NELTS * sizeof (TYPE). (SIZE could be
+   greater when the array under construction requires a cookie to store
+   NELTS.  GCC's placement new expression stores the cookie when invoking
+   a user-defined placement new operator function but not the default one.
+   Placement new expressions with user-defined placement new operator are
+   not diagnosed since we don't know how they use the buffer (this could
+   be a future extension).  */
+static void
+warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
+{
+  location_t loc = EXPR_LOC_OR_LOC (oper, input_location);
+
+  /* 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;
+  /* 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;
+  /* True when the reference to the destination buffer is an ADDR_EXPR.  */
+  bool addr_expr = false;
+
+  STRIP_NOPS (oper);
+
+  /* Using a function argument or a (non-array) variable as an argument
+     to placement new is not checked since it's unknown what it might
+     point to.  */
+  if (TREE_CODE (oper) == PARM_DECL
+      || TREE_CODE (oper) == VAR_DECL
+      || TREE_CODE (oper) == COMPONENT_REF)
+    return;
+
+  /* Evaluate any constant expressions.  */
+  size = fold_non_dependent_expr (size);
+
+  /* Handle the common case of array + offset expression when the offset
+     is a constant.  */
+  if (TREE_CODE (oper) == POINTER_PLUS_EXPR)
+    {
+      /* If the offset is comple-time constant, use it to compute a more
+        accurate estimate of the size of the buffer.  Otherwise, use
+        the size of the entire array as an optimistic estimate (this
+        may lead to false negatives).  */
+      const_tree adj = TREE_OPERAND (oper, 1);
+      if (CONSTANT_CLASS_P (adj))
+       adjust += tree_to_uhwi (adj);
+      else
+       use_obj_size = true;
+
+      oper = TREE_OPERAND (oper, 0);
+
+      STRIP_NOPS (oper);
+    }
+
+  if (TREE_CODE (oper) == TARGET_EXPR)
+    oper = TREE_OPERAND (oper, 1);
+  else if (TREE_CODE (oper) == ADDR_EXPR)
+    {
+      addr_expr = true;
+      oper = TREE_OPERAND (oper, 0);
+    }
+
+  STRIP_NOPS (oper);
+
+  if (TREE_CODE (oper) == ARRAY_REF)
+    {
+      /* Similar to the offset computed above, see if the array index
+        is a compile-time constant.  If so, and unless the offset was
+        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);
+      if (!use_obj_size && CONSTANT_CLASS_P (adj))
+       adjust += tree_to_shwi (adj);
+      else
+       {
+         use_obj_size = true;
+         adjust = 0;
+       }
+
+      oper = TREE_OPERAND (oper, 0);
+    }
+
+  /* Descend into a struct or union to find the member whose address
+     is being used as the agument.  */
+  while (TREE_CODE (oper) == COMPONENT_REF)
+    oper = TREE_OPERAND (oper, 1);
+
+  if ((addr_expr || !POINTER_TYPE_P (TREE_TYPE (oper)))
+      && (TREE_CODE (oper) == VAR_DECL
+         || TREE_CODE (oper) == FIELD_DECL
+         || TREE_CODE (oper) == PARM_DECL))
+    {
+      /* A possibly optimistic estimate of the number of bytes available
+        in the destination buffer.  */
+      unsigned HOST_WIDE_INT bytes_avail;
+      /* True when the estimate above is in fact the exact size
+        of the destination buffer rather than an estimate.  */
+      bool exact_size = true;
+
+      /* Treat members of unions and members of structs uniformly, even
+        though the size of a member of a union may be viewed as extending
+        to the end of the union itself (it is by __builtin_object_size).  */
+      if (TREE_CODE (oper) == VAR_DECL || use_obj_size)
+       {
+         /* 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_shwi (DECL_SIZE_UNIT (oper));
+         exact_size = !use_obj_size;
+       }
+      else
+       {
+         /* 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)));
+       }
+
+      /* Avoid diagnosing flexible array members (accepted as an extension
+        and diagnosed with -Wpedantic).
+        Constructing objects that appear to overflow the C99 equivalent of
+        flexible array members (i.e., array members of size zero or one)
+        are diagnosed in C++ since their declaration cannot be diagnosed.  */
+      if (bytes_avail == 0 && TREE_CODE (TREE_TYPE (oper)) == ARRAY_TYPE)
+       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))
+       bytes_avail = 0;
+      else
+       bytes_avail -= adjust;
+
+      /* 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;
+
+      if (CONSTANT_CLASS_P (size))
+       bytes_need = tree_to_uhwi (size);
+      else if (nelts && CONSTANT_CLASS_P (nelts))
+         bytes_need = tree_to_uhwi (nelts)
+           * tree_to_uhwi (TYPE_SIZE_UNIT (type));
+      else
+       bytes_need = tree_to_uhwi (TYPE_SIZE_UNIT (type));
+
+      if (bytes_avail < bytes_need)
+       {
+         if (nelts)
+           if (CONSTANT_CLASS_P (nelts))
+             warning_at (loc, OPT_Wplacement_new,
+                         exact_size ?
+                         "placement new constructing an object of type "
+                         "%<%T [%wu]%> and size %qwu in a region of type %qT "
+                         "and size %qwi"
+                         : "placement new constructing an object of type "
+                         "%<%T [%lu]%> 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);
+           else
+             warning_at (loc, OPT_Wplacement_new,
+                         exact_size ?
+                         "placement new constructing an array of objects "
+                         "of type %qT and size %qwu in a region of type %qT "
+                         "and size %qwi"
+                         : "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);
+         else
+           warning_at (loc, OPT_Wplacement_new,
+                       exact_size ?
+                       "placement new constructing an object of type %qT "
+                       "and size %qwu in a region of type %qT and size %qwi"
+                       : "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);
+       }
+    }
+}
+
 /* 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
@@ -2525,6 +2720,8 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
       && (TYPE_PTR_P (TREE_TYPE ((**placement)[0]))))
     placement_first = (**placement)[0];
 
+  bool member_new_p = false;
+
   /* Allocate the object.  */
   if (vec_safe_is_empty (*placement) && TYPE_FOR_JAVA (elt_type))
     {
@@ -2573,11 +2770,13 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 
       fnname = ansi_opname (array_p ? VEC_NEW_EXPR : NEW_EXPR);
 
-      if (!globally_qualified_p
+      member_new_p = !globally_qualified_p
          && CLASS_TYPE_P (elt_type)
          && (array_p
              ? TYPE_HAS_ARRAY_NEW_OPERATOR (elt_type)
-             : TYPE_HAS_NEW_OPERATOR (elt_type)))
+           : TYPE_HAS_NEW_OPERATOR (elt_type));
+
+      if (member_new_p)
        {
          /* Use a class-specific operator new.  */
          /* If a cookie is required, add some extra space.  */
@@ -2652,20 +2851,30 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
   /* If we found a simple case of PLACEMENT_EXPR above, then copy it
      into a temporary variable.  */
   if (!processing_template_decl
-      && placement_first != NULL_TREE
       && TREE_CODE (alloc_call) == CALL_EXPR
       && call_expr_nargs (alloc_call) == 2
       && TREE_CODE (TREE_TYPE (CALL_EXPR_ARG (alloc_call, 0))) == INTEGER_TYPE
       && TYPE_PTR_P (TREE_TYPE (CALL_EXPR_ARG (alloc_call, 1))))
     {
-      tree placement_arg = CALL_EXPR_ARG (alloc_call, 1);
+      tree placement = CALL_EXPR_ARG (alloc_call, 1);
 
-      if (INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (TREE_TYPE (placement_arg)))
-         || VOID_TYPE_P (TREE_TYPE (TREE_TYPE (placement_arg))))
+      if (placement_first != NULL_TREE
+         && (INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (TREE_TYPE (placement)))
+             || VOID_TYPE_P (TREE_TYPE (TREE_TYPE (placement)))))
        {
          placement_expr = get_target_expr (placement_first);
          CALL_EXPR_ARG (alloc_call, 1)
-           = convert (TREE_TYPE (placement_arg), placement_expr);
+           = convert (TREE_TYPE (placement), placement_expr);
+       }
+
+      if (!member_new_p
+         && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (CALL_EXPR_ARG (alloc_call, 1)))))
+       {
+         /* Attempt to make the warning point at the operator new argument.  */
+         if (placement_first)
+           placement = placement_first;
+
+         warn_placement_new_too_small (orig_type, nelts, size, placement);
        }
     }
 
index 4fc7d887a72a4666da3c171e7517d334b4ebfc0a..587e30e613defa8cd7f982361cb027e1493bf5d2 100644 (file)
@@ -272,7 +272,7 @@ Objective-C and Objective-C++ Dialects}.
 -Woverride-init-side-effects @gol
 -Woverlength-strings  -Wpacked  -Wpacked-bitfield-compat  -Wpadded @gol
 -Wparentheses  -Wpedantic-ms-format -Wno-pedantic-ms-format @gol
--Wpointer-arith  -Wno-pointer-to-int-cast @gol
+-Wplacement-new -Wpointer-arith  -Wno-pointer-to-int-cast @gol
 -Wredundant-decls  -Wno-return-local-addr @gol
 -Wreturn-type  -Wsequence-point  -Wshadow  -Wno-shadow-ivar @gol
 -Wshift-overflow -Wshift-overflow=@var{n} @gol
@@ -4870,6 +4870,13 @@ disables the warnings about non-ISO @code{printf} / @code{scanf} format
 width specifiers @code{I32}, @code{I64}, and @code{I} used on Windows targets,
 which depend on the MS runtime.
 
+@item -Wplacement-new
+@opindex Wplacement-new
+@opindex Wno-placement-new
+Warn about placement new expressions with undefined behavior, such as
+constructing an object in a buffer that is smaller than the type of
+the object.
+
 @item -Wpointer-arith
 @opindex Wpointer-arith
 @opindex Wno-pointer-arith
index d41124e91cf34b24ec97738a1ad0baa0c548fd77..c0a0d78ccc6c4a2d7f7de2fa30c96c9e1b3e56dc 100644 (file)
@@ -1,3 +1,8 @@
+2015-11-05  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/67942
+       * g++.dg/warn/Wplacement-new-size.C: New test.
+
 2015-11-05  Alan Lawrence  <alan.lawrence@arm.com>
 
        * gcc.dg/pr68112.c: New.
diff --git a/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
new file mode 100644 (file)
index 0000000..a8a2a68
--- /dev/null
@@ -0,0 +1,410 @@
+/* { dg-do compile } */
+/* { dg-options "-Wplacement-new -fpermissive" } */
+
+typedef __typeof__ (sizeof 0) size_t;
+
+void* operator new (size_t, void *p) { return p; }
+void* operator new[] (size_t, void *p) { return p; }
+
+static __attribute__ ((used))char c;
+static __attribute__ ((used))char ac1 [1];
+static __attribute__ ((used))char ac2 [2];
+static __attribute__ ((used))char ac3 [3];
+static __attribute__ ((used))char ac4 [4];
+static __attribute__ ((used))char ac5 [5];
+static __attribute__ ((used))char ac6 [6];
+static __attribute__ ((used))char ac7 [7];
+static __attribute__ ((used))char ac8 [8];
+
+static __attribute__ ((used))char ac1_1 [1][1];
+static __attribute__ ((used))char ac1_2 [1][2];
+static __attribute__ ((used))char ac2_1 [2][1];
+static __attribute__ ((used))char ac2_2 [2][2];
+
+static __attribute__ ((used))short s;
+static __attribute__ ((used))short as1 [1];
+static __attribute__ ((used))short as2 [2];
+
+static __attribute__ ((used))struct SC { char c; char *pc; void *pv; } sc;
+static __attribute__ ((used))struct SAC1 { char ac [1]; } sac1;
+static __attribute__ ((used))struct SAC2 { char ac [2]; } sac2;
+static __attribute__ ((used))struct SAC3 { char ac [3]; } sac3;
+static __attribute__ ((used))struct SAC4 { char ac [4]; } sac4;
+
+static __attribute__ ((used))struct SSC { SC sc; int x; } ssc;
+static __attribute__ ((used))struct SSAC1 { SAC1 sac; } ssac1;
+static __attribute__ ((used))struct SSAC2 { SAC2 sac; } ssac2;
+static __attribute__ ((used))struct SSAC3 { SAC3 sac; } ssac3;
+static __attribute__ ((used))struct SSAC4 { SAC4 sac; } ssac4;
+
+static __attribute__ ((used))struct SSAC4_2 { SSAC4 ssac4_2 [2]; } sssac4_2;
+
+static __attribute__ ((used))union UAC1 { char c; char ac [1]; } uac1;
+static __attribute__ ((used))union UAC2 { char c; char ac [2]; } uac2;
+static __attribute__ ((used))union UAC3 { char c; char ac [3]; } uac3;
+static __attribute__ ((used))union UAC4 { char c; char ac [4]; } uac4;
+
+static __attribute__ ((used))SC fsc ()  { return SC (); }
+static __attribute__ ((used))SAC1 fasc1 () { return SAC1 (); }
+static __attribute__ ((used))SAC2 fasc2 () { return SAC2 (); }
+static __attribute__ ((used))SAC3 fasc3 () { return SAC3 (); }
+static __attribute__ ((used))SAC4 fasc4 () { return SAC4 (); }
+
+static __attribute__ ((used))void *r;
+
+static __attribute__ ((used))void* ptr () { return 0; }
+
+static __attribute__ ((used))
+void test (void *p, int n)
+{
+    {
+        void *q = p;
+        struct { void *p; } s = { p };
+
+        // Verify that none of function arguments, local or global
+        // variables, or function return values trigger the warning.
+        new (p) char;
+        new (q) char;
+        new (r) char;
+        new (s.p) char;
+        new (ptr ()) char;
+
+        new (p) char [32];
+        new (q) char [32];
+        new (r) char [32];
+        new (s.p) char [32];
+        new (ptr ()) char [32];
+
+        new (&p) char;
+        new (&q) char;
+        new (&r) char;
+
+        // Using address of objects, however, must trigger the warning.
+        new (&p) char [32];                 // { dg-warning "placement" }
+        new (&q) char [32];                 // { dg-warning "placement" }
+        new (&r) char [32];                 // { dg-warning "placement" }
+    }
+
+    enum { N0, N1, N2, N3 };
+
+    new (&c) char;
+
+    // Warn for the common case when constructing at an offset from
+    // the beginning of an array that doesn't leave enough space for
+    // the object.
+    new (&c + 0) char;                  // okay
+    new (&c + n) char;                  // okay (n is unknown)
+    new (&c + 1) char;                  // { dg-warning "placement" }
+    new (&c + N0) char;
+    new (&c + N1) char;                 // { dg-warning "placement" }
+
+    // No warning is issued when constructing an array in space exactly
+    // its size even though strictly speaking a compiler is allowed to
+    // add a cookie to the array (gcc does not).
+    new (&c) char [1];
+    new (&c) char [sizeof c];
+    new (&c) char [n];
+    new (&c) char [1][1];
+    new (&c) char [1][1][1];
+    new (&c + N1) char [1][1][1];       // { dg-warning "placement" }
+
+    new (&c) char [2];                  // { dg-warning "placement" }
+    new (&c) char [sizeof c + 1];       // { dg-warning "placement" }
+    new (&c) char [1][2];               // { dg-warning "placement" }
+    new (&c) char [2][1];               // { dg-warning "placement" }
+    new (&c) char [n][1];
+    new (&c) char [n][2];               // { dg-warning "placement" }
+    new (&c) char [3];                  // { dg-warning "placement" }
+    new (&c) char [3][1];               // { dg-warning "placement" }
+    new (&c) char [1][3];               // { dg-warning "placement" }
+    new (&c) char [4][1];               // { dg-warning "placement" }
+    new (&c) char [1][4];               // { dg-warning "placement" }
+
+    // Casts must not suppress it.
+    new ((void*)&c) char [2];           // { dg-warning "placement" }
+    new ((char*)&c) char [3];           // { dg-warning "placement" }
+
+    new (static_cast<void*>(&c)) char [4];        // { dg-warning "placement" }
+    new (reinterpret_cast<char*>(&c)) char [5];   // { dg-warning "placement" }
+
+    new (&c + 0) char [2];              // { dg-warning "placement" }
+    new (&c + 0) char [3];              // { dg-warning "placement" }
+    new (&c + 0) char [4];              // { dg-warning "placement" }
+
+    new (&c + 1) char [2];              // { dg-warning "placement" }
+    new (&c + 1) char [3];              // { dg-warning "placement" }
+    new (&c + 1) char [4];              // { dg-warning "placement" }
+
+    new (&c + N0) char [1];
+    new (&c + N1) char [2];             // { dg-warning "placement" }
+
+    // Warn even though n is unknown since c is too small for char[2]
+    // regardless of the value of n.
+    new (&c + n) char [2];              // { dg-warning "placement" }
+
+    new (ac2) char [1];
+    new (ac2) char [1][1];
+    new (ac2) char [1][2];
+    new (ac2) char [2][1];
+    new (ac2) char [1][3];              // { dg-warning "placement" }
+    new (ac2) char [2][2];              // { dg-warning "placement" }
+    new (ac2) char [3][1];              // { dg-warning "placement" }
+
+    new (ac2 + N0) char [1][1];
+    new (ac2 + N0) char [1][2];
+    new (ac2 + N1) char [1][2];         // { dg-warning "placement" }
+    new (ac2 + N1) char [2][1];         // { dg-warning "placement" }
+    new (ac2 + N2) char [1][1];         // { dg-warning "placement" }
+    new (ac2 + N2) char [1][2];         // { dg-warning "placement" }
+    new (ac2 + N2) char [2][1];         // { dg-warning "placement" }
+    new (ac2 + N2) char [2][2];         // { dg-warning "placement" }
+
+    new (ac8) char [1];
+    new (ac8) char [2][2];
+    new (ac8) char [2][3];
+    new (ac8) char [2][4];
+    new (ac8) char [2][5];              // { dg-warning "placement" }
+    new (ac8) char [2][2][2];
+    new (ac8) char [2][2][3];           // { dg-warning "placement" }
+
+    new (&c) int;                       // { dg-warning "placement" }
+
+    new (&ac1) int;                     // { dg-warning "placement" }
+    new (&ac2) int;                     // { dg-warning "placement" }
+    new (&ac3) int;                     // { dg-warning "placement" }
+    new (&ac4) int;
+
+    // Constructing at an address of an array element.
+    new (&ac1 [0]) int;                 // { dg-warning "placement" }
+    new (&ac2 [0]) int;                 // { dg-warning "placement" }
+    new (&ac3 [0]) int;                 // { dg-warning "placement" }
+    new (&ac4 [0]) int;
+
+    // ...plus or minus a constant offset.
+    new (&ac1 [0] + 0) int;             // { dg-warning "placement" }
+    new (&ac2 [0] + 0) int;             // { dg-warning "placement" }
+    new (&ac3 [0] + 0) int;             // { dg-warning "placement" }
+    new (&ac4 [0] + 0) int;
+    new (&ac4 [1] + 0) int;             // { dg-warning "placement" }
+    new (&ac4 [1] - 1) int;
+    new (&ac4 [2] - 1) int;             // { dg-warning "placement" }
+    new (&ac4 [2] - 2) int;
+    new (&ac4 [3] - 1) int;             // { dg-warning "placement" }
+    new (&ac4 [3] - 2) int;             // { dg-warning "placement" }
+    new (&ac4 [3] - 3) int;
+    new (&ac4 [4] - 1) int;             // { dg-warning "placement" }
+    new (&ac4 [4] - 2) int;             // { dg-warning "placement" }
+    new (&ac4 [4] - 3) int;             // { dg-warning "placement" }
+    new (&ac4 [4] - 4) int;
+
+    new (&ac1 [0] + 1) int;             // { dg-warning "placement" }
+    new (&ac2 [0] + 1) int;             // { dg-warning "placement" }
+    new (&ac3 [0] + 1) int;             // { dg-warning "placement" }
+    new (&ac4 [0] + 1) int;             // { dg-warning "placement" }
+
+    new (&ac3 [0] + n) int;             // { dg-warning "placement" }
+    new (&ac4 [0] + n) int;             // no warning (n could be zero)
+    new (&ac4 [1] + n) int;             // no warning (n could be negative)
+    new (&ac4 [2] + n) int;             // ditto
+    new (&ac4 [3] + n) int;             // ditto
+    new (&ac4 [4] + n) int;             // ditto
+    new (&ac4 [4] - n) int;             // (or positive)
+
+    new (&c + 0) int;                   // { dg-warning "placement" }
+    new (&c + 1) int;                   // { dg-warning "placement" }
+
+    // Constructing at an offset into the address of an array.
+    new (&ac1 + 0) int;                 // { dg-warning "placement" }
+    new (&ac1 + 1) int;                 // { dg-warning "placement" }
+    new (&ac1 + n) int;                 // { dg-warning "placement" }
+    new (&ac2 + 0) int;                 // { dg-warning "placement" }
+    new (&ac2 + 1) int;                 // { dg-warning "placement" }
+    new (&ac2 + n) int;                 // { dg-warning "placement" }
+    new (&ac3 + 0) int;                 // { dg-warning "placement" }
+    new (&ac3 + 1) int;                 // { dg-warning "placement" }
+
+    // Even though n below is uknown an array of 3 bytes isn't large
+    // enugh for an int.
+    new (&ac3 + n) int;                 // { dg-warning "placement" }
+
+    new (&ac4 + 0) int;
+    new (&ac4 + 1) int;                 // { dg-warning "placement" }
+    new (&ac4 + n) int;                 // no warning (n could be zero)
+
+    // Constructing in an array object.
+    new (ac1) int;                      // { dg-warning "placement" }
+    new (ac2) int;                      // { dg-warning "placement" }
+    new (ac3) int;                      // { dg-warning "placement" }
+    new (ac4) int;
+    new (ac5) int;
+    new (ac5 + 0) int;
+    new (ac5 + 1) int;
+    new (ac5 + n) int;                  // no warning (n could be zero)
+    new (ac5 + 2) int;                  // { dg-warning "placement" }
+    new (ac5 + 3) int;                  // { dg-warning "placement" }
+    new (ac5 + 4) int;                  // { dg-warning "placement" }
+    new (ac5 + 5) int;                  // { dg-warning "placement" }
+
+    new (ac1_1) char;
+    new (ac1_1) char[1];
+    new (ac1_1) char[n];                // no warning (n is unknown)
+    new (ac1_1) char[2];                // { dg-warning "placement" }
+    new (ac1_1) char[3];                // { dg-warning "placement" }
+
+    new (ac1_2) char;
+    new (ac1_2) char[1];
+    new (ac1_2) char[2];
+    new (ac1_2) char[3];                // { dg-warning "placement" }
+
+    new (ac2_1) char;
+    new (ac2_1) char[1];
+    new (ac2_1) char[2];
+    new (ac2_1) char[3];                // { dg-warning "placement" }
+
+    new (ac2_2) char;
+    new (ac2_2) char[1];
+    new (ac2_2) char[2];
+    new (ac2_2) char[2][2];
+
+    // Even though n below is uknown it can't meaningfully be zero
+    // (even if zero-size arrays are allowed as an extension, the size
+    // they are allocated in by placement new is zero).
+    new (ac1_1) char[n][2];             // { dg-warning "placement" }
+    new (ac2_2) char[3];
+    new (ac2_2) char[3][1];
+    new (ac2_2) char[3][2];             // { dg-warning "placement" }
+    new (ac2_2) char[4];
+    new (ac2_2) char[4][1];
+    new (ac2_2) char[4][2];             // { dg-warning "placement" }
+    new (ac2_2) char[5];                // { dg-warning "placement" }
+
+    new (&s) int;                       // { dg-warning "placement" }
+    new (&as1) int;                     // { dg-warning "placement" }
+    new (&as2) int;
+
+    new (as1) int;                      // { dg-warning "placement" }
+    new (as2) int;
+
+    new (&sc.c) int;                    // { dg-warning "placement" }
+    new (&sac1.ac) int;                 // { dg-warning "placement" }
+    new (&sac2.ac) int;                 // { dg-warning "placement" }
+    new (&sac3.ac) int;                 // { dg-warning "placement" }
+    new (&sac4.ac) int;
+
+    new (sc.pc) char;
+    new (sc.pc) int;
+    new (sc.pc) int[1024];
+    new (sc.pc + 0) int;
+    new (sc.pc + 0) int[2048];
+    new (sc.pv) int;
+    new (sc.pv) char[1024];
+
+    new (sac1.ac) int;                  // { dg-warning "placement" }
+    new (sac2.ac) int;                  // { dg-warning "placement" }
+    new (sac3.ac) int;                  // { dg-warning "placement" }
+    new (sac4.ac) int;
+
+    new (&ssc.sc) SSC;                  // { dg-warning "placement" }
+    new (&ssac1.sac) int;               // { dg-warning "placement" }
+    new (&ssac2.sac) int;               // { dg-warning "placement" }
+    new (&ssac3.sac) int;               // { dg-warning "placement" }
+    new (&ssac4.sac) int;
+
+    new (&sssac4_2) char[sizeof sssac4_2];
+    new (&sssac4_2) char[sizeof sssac4_2 + 1];   // { dg-warning "placement" }
+
+    // taking the address of a temporary is allowed with -fpermissive
+    new (&fsc ().c) int;                // { dg-warning "address|placement" }
+    new (&fasc1 ().ac) int;             // { dg-warning "address|placement" }
+    new (&fasc2 ().ac) int;             // { dg-warning "address|placement" }
+    new (&fasc3 ().ac) int;             // { dg-warning "address|placement" }
+    new (&fasc4 ().ac) int;             // { dg-warning "address|placement" }
+
+    new (&uac1) int;                    // { dg-warning "placement" }
+    new (&uac2) int;                    // { dg-warning "placement" }
+    new (&uac3) int;                    // { dg-warning "placement" }
+    new (&uac4) int;
+    new (&uac4 + 1) int;                // { dg-warning "placement" }
+
+    new (&uac1.c) int;                  // { dg-warning "placement" }
+    new (&uac2.c) int;                  // { dg-warning "placement" }
+    new (&uac3.c) int;                  // { dg-warning "placement" }
+
+    // Diagnose the following even though the size of uac4.c could be
+    // expected to extend to the end of the union (as it is by Built-in
+    // Object Size and so isn't diagnosed in calls to functions like
+    // memset(&uac4.c, 0, sizeof(int)) when _FORTIFY_SOURCE is non-zero.  */
+    new (&uac4.c) int;                  // { dg-warning "placement" }
+
+    new (&uac4.c + 1) int;              // { dg-warning "placement" }
+}
+
+
+struct S { char c [2]; };
+
+// Verify the full text of the warning message.
+static  __attribute__ ((used))
+void test_message (int i)
+{
+    char a [2];
+
+    // The exact sizes of both the buffer and the type are known.
+    new (a + 1) S;         // { dg-warning "placement new constructing an object of type .S. and size .2. in a region of type .char \\\[2\\\]. and size .1." }
+
+    // The buffer size is known but only the size of the type whose
+    // objects are being constructed is known, not their number.  While
+    // in theory it could be zero, it practice likely never will be so
+    // the potential false positive is acceptable.
+    new (a + 1) S [i];  // { dg-warning "placement new constructing an array of objects of type .S. and size .2. in a region of type .char \\\[2\\\]. and size .1." }
+
+    // The exact amount of space in the buffer isn't known, only its
+    // maximum is.  The exact size of the array being created is known.
+    new (a + i) S [2];  // { dg-warning "placement new constructing an object of type .S \\\[2\\\]. and size .4. in a region of type .char \\\[2\\\]. and size at most .2." }
+}
+
+
+struct ClassWithMemberNew {
+    struct Object { int i; } *pobj;
+    unsigned nobj;
+
+    ClassWithMemberNew ();
+    void foo ();
+
+    void* operator new (size_t, void*);
+    void* operator new[] (size_t, void*);
+};
+
+void ClassWithMemberNew::foo()
+{
+    for (unsigned i = 0; i != nobj; ++i)
+        new (pobj + i) Object ();
+}
+
+
+struct ClassWithGlobalNew {
+    int a [4];
+    ClassWithGlobalNew ();
+};
+
+void* operator new (size_t, ClassWithGlobalNew*);
+void* operator new[] (size_t, ClassWithGlobalNew*);
+
+void test_user_defined_placement_new ()
+{
+    {
+        ClassWithMemberNew x;
+
+        // Expect no diagnostics for placement new expressions with types
+        // with their own placement operator new since the semantics of
+        // the operator aren't known.
+        new (&c) ClassWithMemberNew;
+        new (&x) ClassWithMemberNew[2];
+    }
+
+    {
+        ClassWithGlobalNew x;
+
+        new (&c) ClassWithGlobalNew;    // { dg-warning "placement" }
+        new (&x) ClassWithGlobalNew[2];
+    }
+}