public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++/67942 - diagnose placement new buffer overflow
@ 2015-10-13 17:23 Martin Sebor
  2015-10-21  0:03 ` [PING] " Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-10-13 17:23 UTC (permalink / raw)
  To: Gcc Patch List

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

C++ placement new expression is susceptible to buffer overflow flaws
(see [1]).  In many such cases GCC has sufficient information to
detect and diagnose such defects. The attached patch is a starting
point for this feature.  It lets GCC diagnose basic cases of buffer
overflows when both the size of the buffer and the type being
constructed are constant expressions.  A more sophisticated
implementation would try to detect additional cases in a manner
similar to _FORTIFY_SOURCE.

Besides buffer overflow, placement new can also be misused to
construct objects in unaligned storage (also discussed in the paper
below).  I leave diagnosing such cases and improving the detection
of buffer overflows via a mechanism like Object Size Checking for
a future patch.

Tested on x86_64 with no regressions.

Martin

[1] A New Class of Buffer Overflow Attacks, Kundu, A., Bertino, E.,
31st International Conference on Distributed Computing Systems (ICDCS),
2011 http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=5961725


[-- Attachment #2: gcc-67942-placement-new-overflow.patch --]
[-- Type: text/x-patch, Size: 27152 bytes --]

gcc ChangeLog
2015-10-12  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.

gcc/c-family ChangeLog
2015-10-12  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
        * c.opt (-Wplacement-new): New option.

gcc/cp ChangeLog
2015-10-12  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
	* cp/init.c (warn_placement_new_too_small): New function.
	(build_new_1): Call it.

iff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 47ba070..5e9d7a3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -760,6 +760,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
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..9d23fea 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2269,6 +2269,183 @@ 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, in
+   theory, be greater when the array under construction requires a cookie
+   to store NELTS, but GCC's placement new does not store the cookie.  */
+static void
+warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
+{
+  const_tree orig_oper = oper;
+
+  /* The number of bytes to add or subtract from the size of the provided
+     buffer based on an offset into an array or an array element reference.  */
+  HOST_WIDE_INT adjust = 0;
+  bool addr_expr = false;
+  bool use_obj_size = false;
+
+  while (TREE_CODE (oper) == NOP_EXPR)
+    oper = TREE_OPERAND (oper, 0);
+
+  /* Using a function argument or a (non-array) variable as an argument
+     to placement new is not checked since it's unknownwhat 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 += (HOST_WIDE_INT)tree_to_uhwi (adj);
+      else
+	use_obj_size = true;
+
+      oper = TREE_OPERAND (oper, 0);
+
+      while (TREE_CODE (oper) == NOP_EXPR)
+	oper = TREE_OPERAND (oper, 0);
+    }
+
+  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);
+  }
+
+  while (TREE_CODE (oper) == NOP_EXPR)
+    oper = TREE_OPERAND (oper, 0);
+
+  if (TREE_CODE (oper) == ARRAY_REF)
+    {
+      // fold_array_ref (oper);
+
+      /* 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 = false;
+	}
+
+      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))
+    {
+      HOST_WIDE_INT bytes_avail;
+      const char* txt = "";
+
+      /* When the referenced object is a member of a union, use the size
+	 of the entire union as the size of the buffer.  */
+      if (TREE_CODE (oper) == FIELD_DECL
+	  && TREE_CODE (DECL_CONTEXT (oper)) == UNION_TYPE)
+	bytes_avail = tree_to_shwi (TYPE_SIZE_UNIT (DECL_CONTEXT (oper)));
+      else 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));
+	  txt = "at most ";
+	}
+      else
+	bytes_avail = tree_to_shwi (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;
+
+      /* Reduce the size of the buffer by the adjustment computed above
+	 from the offset and/or the index into the array.  */
+      if (bytes_avail <= abs (adjust))
+	bytes_avail = 0;
+      else if (0 <= adjust)
+	bytes_avail -= adjust;
+      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 (static_cast<unsigned HOST_WIDE_INT>(bytes_avail) < bytes_need)
+	{
+	  if (nelts)
+	    if (CONSTANT_CLASS_P (nelts))
+	      warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
+			  OPT_Wplacement_new,
+			  "placement new constructing a%s %ld-byte object "
+			  "of type %<%T[%lu]%> in a region of type %qT "
+			  "that is %s%ld byte%s large",
+			  bytes_need % 10 == 8 ? "n" : "", bytes_need,
+			  type, tree_to_uhwi (nelts), TREE_TYPE (oper),
+			  txt, bytes_avail, bytes_avail == 1 ? "" : "s");
+	    else
+	      warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
+			  OPT_Wplacement_new,
+			  "placement new constructing an array of %ld-byte "
+			  "objects of type %qT in a region of type %qT "
+			  "that is %s%ld byte%s large",
+			  bytes_need,
+			  type, TREE_TYPE (oper),
+			  txt, bytes_avail, bytes_avail == 1 ? "" : "s");
+	  else
+	    warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
+			OPT_Wplacement_new,
+			"placement new constructing a%s %ld-byte object "
+			"of type %qT in a region of type %qT "
+			"that is %s%ld byte%s large",
+			bytes_need % 10 == 8 ? "n" : "", bytes_need,
+			type, TREE_TYPE (oper),
+			txt, bytes_avail, bytes_avail == 1 ? "" : "s");
+	}
+    }
+}
+
 /* 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
@@ -2518,6 +2695,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))
     {
@@ -2566,11 +2745,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.  */
@@ -2645,20 +2826,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);
 	}
     }

@@ -3066,6 +3257,7 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
           else
             return error_mark_node;
         }
+
       nelts = mark_rvalue_use (nelts);
       nelts = cp_save_expr (cp_convert (sizetype, nelts, complain));
     }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 547ee2d..4f89fa1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -271,7 +271,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
@@ -4789,6 +4789,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
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
index 0000000..d7ceb2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
@@ -0,0 +1,380 @@
+/* { 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 char c;
+static char ac1 [1];
+static char ac2 [2];
+static char ac3 [3];
+static char ac4 [4];
+static char ac5 [5];
+static char ac6 [6];
+static char ac7 [7];
+static char ac8 [8];
+
+static char ac1_1 [1][1];
+static char ac1_2 [1][2];
+static char ac2_1 [2][1];
+static char ac2_2 [2][2];
+
+static short s;
+static short as1 [1];
+static short as2 [2];
+
+static struct SC { char c; char *pc; void *pv; } sc;
+static struct SAC1 { char ac [1]; } sac1;
+static struct SAC2 { char ac [2]; } sac2;
+static struct SAC3 { char ac [3]; } sac3;
+static struct SAC4 { char ac [4]; } sac4;
+
+static struct SSC { SC sc; int x; } ssc;
+static struct SSAC1 { SAC1 sac; } ssac1;
+static struct SSAC2 { SAC2 sac; } ssac2;
+static struct SSAC3 { SAC3 sac; } ssac3;
+static struct SSAC4 { SAC4 sac; } ssac4;
+
+static struct SSAC4_2 { SSAC4 ssac4_2 [2]; } sssac4_2;
+
+static union UAC1 { char c; char ac [1]; } uac1;
+static union UAC2 { char c; char ac [2]; } uac2;
+static union UAC3 { char c; char ac [3]; } uac3;
+static union UAC4 { char c; char ac [4]; } uac4;
+
+static SC fsc ()  { return SC (); }
+static SAC1 fasc1 () { return SAC1 (); }
+static SAC2 fasc2 () { return SAC2 (); }
+static SAC3 fasc3 () { return SAC3 (); }
+static SAC4 fasc4 () { return SAC4 (); }
+
+static void *r;
+
+static void* ptr () { return 0; }
+
+static 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" }
+    new (&uac4.c) int;
+    new (&uac4.c + 1) int;              // { dg-warning "placement" }
+}
+
+
+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];
+    }
+}

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

* [PING] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-10-13 17:23 [PATCH] c++/67942 - diagnose placement new buffer overflow Martin Sebor
@ 2015-10-21  0:03 ` Martin Sebor
  2015-10-27  2:07   ` [PING 2] " Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-10-21  0:03 UTC (permalink / raw)
  To: Gcc Patch List

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

Attached is a slightly updated patch that tweaks the diagnostic
messages to avoid assuming the English punctuation, and adds
a few test cases exercising the text of the diagnostics.

Martin

On 10/13/2015 11:22 AM, Martin Sebor wrote:
> C++ placement new expression is susceptible to buffer overflow flaws
> (see [1]).  In many such cases GCC has sufficient information to
> detect and diagnose such defects. The attached patch is a starting
> point for this feature.  It lets GCC diagnose basic cases of buffer
> overflows when both the size of the buffer and the type being
> constructed are constant expressions.  A more sophisticated
> implementation would try to detect additional cases in a manner
> similar to _FORTIFY_SOURCE.
>
> Besides buffer overflow, placement new can also be misused to
> construct objects in unaligned storage (also discussed in the paper
> below).  I leave diagnosing such cases and improving the detection
> of buffer overflows via a mechanism like Object Size Checking for
> a future patch.
>
> Tested on x86_64 with no regressions.
>
> Martin
>
> [1] A New Class of Buffer Overflow Attacks, Kundu, A., Bertino, E.,
> 31st International Conference on Distributed Computing Systems (ICDCS),
> 2011 http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=5961725
>


[-- Attachment #2: gcc-67942-placement-new-overflow.patch --]
[-- Type: text/x-patch, Size: 30001 bytes --]

gcc ChangeLog
2015-10-20  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.

gcc/c-family ChangeLog
2015-10-20  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
        * c.opt (-Wplacement-new): New option.

gcc/cp ChangeLog
2015-10-20  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
	* cp/init.c (warn_placement_new_too_small): New function.
	(build_new_1): Call it.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 47ba070..5e9d7a3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -760,6 +760,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
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..63c7a01 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2269,6 +2269,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, in
+   theory, be greater when the array under construction requires a cookie
+   to store NELTS, but GCC's placement new does not store the cookie.  */
+static void
+warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
+{
+  const_tree orig_oper = oper;
+
+  /* The number of bytes to add or subtract from the size of the provided
+     buffer based on an offset into an array or an array element reference.  */
+  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;
+
+  while (TREE_CODE (oper) == NOP_EXPR)
+    oper = TREE_OPERAND (oper, 0);
+
+  /* Using a function argument or a (non-array) variable as an argument
+     to placement new is not checked since it's unknownwhat 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 += (HOST_WIDE_INT)tree_to_uhwi (adj);
+      else
+	use_obj_size = true;
+
+      oper = TREE_OPERAND (oper, 0);
+
+      while (TREE_CODE (oper) == NOP_EXPR)
+	oper = TREE_OPERAND (oper, 0);
+    }
+
+  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);
+  }
+
+  while (TREE_CODE (oper) == NOP_EXPR)
+    oper = TREE_OPERAND (oper, 0);
+
+  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 = false;
+	}
+
+      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 Number of bytes available
+	 in the destination buffer.  */
+      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;
+
+      /* When the referenced object is a member of a union, use the size
+	 of the entire union as the size of the buffer.  */
+      if (TREE_CODE (oper) == FIELD_DECL
+	  && TREE_CODE (DECL_CONTEXT (oper)) == UNION_TYPE)
+	bytes_avail = tree_to_shwi (TYPE_SIZE_UNIT (DECL_CONTEXT (oper)));
+      else 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_shwi (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;
+
+      /* Reduce the size of the buffer by the adjustment computed above
+	 from the offset and/or the index into the array.  */
+      if (bytes_avail <= abs (adjust))
+	bytes_avail = 0;
+      else if (0 <= adjust)
+	bytes_avail -= adjust;
+      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 (static_cast<unsigned HOST_WIDE_INT>(bytes_avail) < bytes_need)
+	{
+	  if (nelts)
+	    if (CONSTANT_CLASS_P (nelts))
+	      warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
+			  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 %qwi",
+			  type, tree_to_uhwi (nelts), bytes_need,
+			  TREE_TYPE (oper),
+			  bytes_avail);
+	    else
+	      warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
+			  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 %qwi",
+			  type, bytes_need, TREE_TYPE (oper),
+			  bytes_avail);
+	  else
+	    warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
+			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 %qwi",
+			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
@@ -2518,6 +2713,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))
     {
@@ -2566,11 +2763,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.  */
@@ -2645,20 +2844,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);
 	}
     }

@@ -3066,6 +3275,7 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
           else
             return error_mark_node;
         }
+
       nelts = mark_rvalue_use (nelts);
       nelts = cp_save_expr (cp_convert (sizetype, nelts, complain));
     }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 547ee2d..4f89fa1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -271,7 +271,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
@@ -4789,6 +4789,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
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
index 0000000..cef4b0d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
@@ -0,0 +1,404 @@
+/* { 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" }
+    new (&uac4.c) int;
+    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];
+    }
+}

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

* [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-10-21  0:03 ` [PING] " Martin Sebor
@ 2015-10-27  2:07   ` Martin Sebor
  2015-11-03  2:41     ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-10-27  2:07 UTC (permalink / raw)
  To: Gcc Patch List; +Cc: jason

[CC Jason]

When you have a chance, the patch is at the link below for review:
https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02001.html

On 10/20/2015 01:57 PM, Martin Sebor wrote:
> Attached is a slightly updated patch that tweaks the diagnostic
> messages to avoid assuming the English punctuation, and adds
> a few test cases exercising the text of the diagnostics.
>
> Martin
>
> On 10/13/2015 11:22 AM, Martin Sebor wrote:
>> C++ placement new expression is susceptible to buffer overflow flaws
>> (see [1]).  In many such cases GCC has sufficient information to
>> detect and diagnose such defects. The attached patch is a starting
>> point for this feature.  It lets GCC diagnose basic cases of buffer
>> overflows when both the size of the buffer and the type being
>> constructed are constant expressions.  A more sophisticated
>> implementation would try to detect additional cases in a manner
>> similar to _FORTIFY_SOURCE.
>>
>> Besides buffer overflow, placement new can also be misused to
>> construct objects in unaligned storage (also discussed in the paper
>> below).  I leave diagnosing such cases and improving the detection
>> of buffer overflows via a mechanism like Object Size Checking for
>> a future patch.
>>
>> Tested on x86_64 with no regressions.
>>
>> Martin
>>
>> [1] A New Class of Buffer Overflow Attacks, Kundu, A., Bertino, E.,
>> 31st International Conference on Distributed Computing Systems (ICDCS),
>> 2011 http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=5961725
>>
>

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-10-27  2:07   ` [PING 2] " Martin Sebor
@ 2015-11-03  2:41     ` Jason Merrill
  2015-11-04 17:12       ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2015-11-03  2:41 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 10/26/2015 09:48 PM, Martin Sebor wrote:
> +  while (TREE_CODE (oper) == NOP_EXPR)
> +    oper = TREE_OPERAND (oper, 0);

This is STRIP_NOPS.

> +     to placement new is not checked since it's unknownwhat it might

Missing space.

> +  else if (TREE_CODE (oper) == ADDR_EXPR) {

The brace should go on its own line.

> +      /* A possibly optimistic estimate Number of bytes available

Maybe "of the number"?

> +      /* When the referenced object is a member of a union, use the size
> +	 of the entire union as the size of the buffer.  */

Why?  If we're accessing one union member, we should limit the allowed 
space to the size of that member.

> +      if (bytes_avail <= abs (adjust))
> +	bytes_avail = 0;
> +      else if (0 <= adjust)
> +	bytes_avail -= adjust;
> +      else
> +	bytes_avail += adjust;

If adjust is negative, I would think that we would have returned already 
because we were dealing with an offset from a pointer of unknown value.

It also seems that you're being careful to avoid bytes_avail going 
negative, so I wonder why you have it signed and bytes_need unsigned.

> +	      warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),

Let's remember this location early on so you don't need orig_oper.

Jason

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-03  2:41     ` Jason Merrill
@ 2015-11-04 17:12       ` Martin Sebor
  2015-11-04 19:12         ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-11-04 17:12 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

On 11/02/2015 07:40 PM, Jason Merrill wrote:
> On 10/26/2015 09:48 PM, Martin Sebor wrote:
>> +  while (TREE_CODE (oper) == NOP_EXPR)
>> +    oper = TREE_OPERAND (oper, 0);
>
> This is STRIP_NOPS.
>
>> +     to placement new is not checked since it's unknownwhat it might
>
> Missing space.
>
>> +  else if (TREE_CODE (oper) == ADDR_EXPR) {
>
> The brace should go on its own line.
>
>> +      /* A possibly optimistic estimate Number of bytes available
>
> Maybe "of the number"?

Thanks for the review. I've fixed the issues above in the latest
patch (attached).

>
>> +      /* When the referenced object is a member of a union, use the size
>> +     of the entire union as the size of the buffer.  */
>
> Why?  If we're accessing one union member, we should limit the allowed
> space to the size of that member.

I followed the more permissive approach taken by _FORTIFY_SOURCE
where the size of the whole object is used (on the assumption that
we will eventually want to adopt the same mechanism here). For
instance, given:

   union U { char c; int i; } u;

GCC doesn't diagnose:

   memset (&u.c, 0, sizeof u);

so I didn't expect we'd want the following diagnosed either:

   new (&u.c) U ();

But if you think it's preferable to use the size of the member
for this iteration of the placement new warning I can change it.
Can you confirm?

>
>> +      if (bytes_avail <= abs (adjust))
>> +    bytes_avail = 0;
>> +      else if (0 <= adjust)
>> +    bytes_avail -= adjust;
>> +      else
>> +    bytes_avail += adjust;
>
> If adjust is negative, I would think that we would have returned already
> because we were dealing with an offset from a pointer of unknown value.

Adjust is negative when the offset to a buffer of known size is
negative. For example:

     char buf [sizeof (int)];
     new (&buf [1] - 1) int;

>
> It also seems that you're being careful to avoid bytes_avail going
> negative, so I wonder why you have it signed and bytes_need unsigned.
>
>> +          warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
>
> Let's remember this location early on so you don't need orig_oper.

Done.

Martin

[-- Attachment #2: gcc-67942-placement-new-overflow.patch --]
[-- Type: text/x-patch, Size: 30858 bytes --]

gcc ChangeLog
2015-11-04  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.

gcc/c-family ChangeLog
2015-11-04  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
        * c.opt (-Wplacement-new): New option.

gcc/cp ChangeLog
2015-11-04  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
	* cp/init.c (warn_placement_new_too_small): New function.
	(build_new_1): Call it.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 47ba070..5e9d7a3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -760,6 +760,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
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..ebf283d 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2269,6 +2269,200 @@ 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 or subtract from the size of the provided
+     buffer based on an offset into an array or an array element reference.  */
+  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 += (HOST_WIDE_INT)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 = false;
+	}
+
+      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.  */
+      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;
+
+      /* When the referenced object is a member of a union, use the size
+	 of the entire union as the size of the buffer.  */
+      if (TREE_CODE (oper) == FIELD_DECL
+	  && TREE_CODE (DECL_CONTEXT (oper)) == UNION_TYPE)
+	bytes_avail = tree_to_shwi (TYPE_SIZE_UNIT (DECL_CONTEXT (oper)));
+      else 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_shwi (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;
+
+      /* Reduce the size of the buffer by the adjustment computed above
+	 from the offset and/or the index into the array.  */
+      if (bytes_avail <= abs (adjust))
+	bytes_avail = 0;
+      else if (0 <= adjust)
+	bytes_avail -= adjust;
+      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 (static_cast<unsigned HOST_WIDE_INT>(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 %qwi",
+			  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 %qwi",
+			  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 %qwi",
+			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
@@ -2518,6 +2712,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))
     {
@@ -2566,11 +2762,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.  */
@@ -2645,20 +2843,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);
 	}
     }

@@ -3066,6 +3274,7 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
           else
             return error_mark_node;
         }
+
       nelts = mark_rvalue_use (nelts);
       nelts = cp_save_expr (cp_convert (sizetype, nelts, complain));
     }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 547ee2d..337639e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -271,7 +271,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
@@ -1506,6 +1506,16 @@ options, an indication is given as to whether the option is enabled,
 disabled or set to a specific value (assuming that the compiler
 knows this at the point where the @option{--help=} option is used).

+It's important to note that when a given optimization option is
+enabled, either explicitly on the command line or implicitly, whether
+or not the optimization it controls will be performed during an
+invocation of the compiler may depend on other options.  This is
+so because many options control various finer aspects of other more
+general optimizations that must be enabled in order for the former
+option to have any effect.  There is currently no command line
+interface to determine such dependencies between optimization
+options.
+
 Here is a truncated example from the ARM port of @command{gcc}:

 @smallexample
@@ -4789,6 +4799,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
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
index 0000000..cef4b0d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
@@ -0,0 +1,404 @@
+/* { 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" }
+    new (&uac4.c) int;
+    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];
+    }
+}

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-04 17:12       ` Martin Sebor
@ 2015-11-04 19:12         ` Jason Merrill
  2015-11-05  0:15           ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2015-11-04 19:12 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 11/04/2015 12:11 PM, Martin Sebor wrote:
> On 11/02/2015 07:40 PM, Jason Merrill wrote:
>> On 10/26/2015 09:48 PM, Martin Sebor wrote:
>>> +  while (TREE_CODE (oper) == NOP_EXPR)
>>> +    oper = TREE_OPERAND (oper, 0);
>>
>> This is STRIP_NOPS.
>>
>>> +     to placement new is not checked since it's unknownwhat it might
>>
>> Missing space.
>>
>>> +  else if (TREE_CODE (oper) == ADDR_EXPR) {
>>
>> The brace should go on its own line.
>>
>>> +      /* A possibly optimistic estimate Number of bytes available
>>
>> Maybe "of the number"?
>
> Thanks for the review. I've fixed the issues above in the latest
> patch (attached).
>
>>
>>> +      /* When the referenced object is a member of a union, use the
>>> size
>>> +     of the entire union as the size of the buffer.  */
>>
>> Why?  If we're accessing one union member, we should limit the allowed
>> space to the size of that member.
>
> I followed the more permissive approach taken by _FORTIFY_SOURCE
> where the size of the whole object is used (on the assumption that
> we will eventually want to adopt the same mechanism here). For
> instance, given:
>
>    union U { char c; int i; } u;
>
> GCC doesn't diagnose:
>
>    memset (&u.c, 0, sizeof u);
>
> so I didn't expect we'd want the following diagnosed either:
>
>    new (&u.c) U ();
>
> But if you think it's preferable to use the size of the member
> for this iteration of the placement new warning I can change it.
> Can you confirm?

There was a lot of discussion of C++ aliasing rules at the recent 
meeting; we really seem to be moving in the direction of being stricter 
about which union member is active.  So I think we do want to diagnose 
the new-expression above; the user should write new (&u) if that's what 
they mean.

>>> +      if (bytes_avail <= abs (adjust))
>>> +    bytes_avail = 0;
>>> +      else if (0 <= adjust)
>>> +    bytes_avail -= adjust;
>>> +      else
>>> +    bytes_avail += adjust;
>>
>> If adjust is negative, I would think that we would have returned already
>> because we were dealing with an offset from a pointer of unknown value.
>
> Adjust is negative when the offset to a buffer of known size is
> negative. For example:
>
>      char buf [sizeof (int)];
>      new (&buf [1] - 1) int;

OK, so because we're looking at the expression from the outside in, we 
first see the subtraction and adjust becomes -1, then we see the 
array_ref and adjust returns to 0.  We still don't have a negative 
adjust by the time we get to the quoted if/else.

>> It also seems that you're being careful to avoid bytes_avail going
>> negative, so I wonder why you have it signed and bytes_need unsigned.
>>
>>> +          warning_at (EXPR_LOC_OR_LOC (orig_oper, input_location),
>>
>> Let's remember this location early on so you don't need orig_oper.
>
> Done.
>
> Martin

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-04 19:12         ` Jason Merrill
@ 2015-11-05  0:15           ` Martin Sebor
  2015-11-05  4:27             ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-11-05  0:15 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

> There was a lot of discussion of C++ aliasing rules at the recent
> meeting; we really seem to be moving in the direction of being stricter
> about which union member is active.  So I think we do want to diagnose
> the new-expression above; the user should write new (&u) if that's what
> they mean.

Okay. I changed that in the latest patch.

>> Adjust is negative when the offset to a buffer of known size is
>> negative. For example:
>>
>>      char buf [sizeof (int)];
>>      new (&buf [1] - 1) int;
>
> OK, so because we're looking at the expression from the outside in, we
> first see the subtraction and adjust becomes -1, then we see the
> array_ref and adjust returns to 0.  We still don't have a negative
> adjust by the time we get to the quoted if/else.

I think I see what you mean. I've changed the type of the variables
and the computation to unsigned. That made it possible to eliminate
the final else and do some other cleanup. Attached is an updated
patch.

Tested on x86_64 by botstrapping C and C++ and running make check.

Martin

[-- Attachment #2: gcc-67942-placement-new-overflow.patch --]
[-- Type: text/x-patch, Size: 30061 bytes --]

gcc ChangeLog
2015-11-04  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.

gcc/c-family ChangeLog
2015-11-04  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
        * c.opt (-Wplacement-new): New option.

gcc/cp ChangeLog
2015-11-04  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
	* cp/init.c (warn_placement_new_too_small): New function.
	(build_new_1): Call it.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 47ba070..5e9d7a3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -760,6 +760,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
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..e2285ec 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2269,6 +2269,199 @@ 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 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 ultimate result cannot be and so the computation is done in
+     unsigned HOST_WIDE_INT.  */
+  unsigned 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_uhwi (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;
+
+      /* Reduce the size of the buffer by the adjustment computed above
+	 from the offset and/or the index into the array.  */
+      if (bytes_avail < 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
@@ -2518,6 +2711,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))
     {
@@ -2566,11 +2761,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.  */
@@ -2645,20 +2842,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);
 	}
     }

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 547ee2d..337639e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -271,7 +271,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
@@ -4789,6 +4799,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
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
index 0000000..a8a2a68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
@@ -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];
+    }
+}

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05  0:15           ` Martin Sebor
@ 2015-11-05  4:27             ` Jason Merrill
  2015-11-05 15:13               ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2015-11-05  4:27 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 11/04/2015 07:15 PM, Martin Sebor wrote:
>> There was a lot of discussion of C++ aliasing rules at the recent
>> meeting; we really seem to be moving in the direction of being stricter
>> about which union member is active.  So I think we do want to diagnose
>> the new-expression above; the user should write new (&u) if that's what
>> they mean.
>
> Okay. I changed that in the latest patch.
>
>>> Adjust is negative when the offset to a buffer of known size is
>>> negative. For example:
>>>
>>>      char buf [sizeof (int)];
>>>      new (&buf [1] - 1) int;
>>
>> OK, so because we're looking at the expression from the outside in, we
>> first see the subtraction and adjust becomes -1, then we see the
>> array_ref and adjust returns to 0.  We still don't have a negative
>> adjust by the time we get to the quoted if/else.
>
> I think I see what you mean. I've changed the type of the variables
> and the computation to unsigned. That made it possible to eliminate
> the final else and do some other cleanup. Attached is an updated
> patch.

Hmm, I was suggesting that bytes_avail change to unsigned, but I don't 
think adjust should change; I believe that 0u - 1u is undefined due to 
overflow even though (-1u) and (unsigned)-1 are well defined.  Sorry for 
the muddled messages.  I think let's leave adjust signed and assert that 
it ends up non-negative.

Jason

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05  4:27             ` Jason Merrill
@ 2015-11-05 15:13               ` Martin Sebor
  2015-11-05 15:27                 ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-11-05 15:13 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

On 11/04/2015 09:27 PM, Jason Merrill wrote:
> On 11/04/2015 07:15 PM, Martin Sebor wrote:
>>> There was a lot of discussion of C++ aliasing rules at the recent
>>> meeting; we really seem to be moving in the direction of being stricter
>>> about which union member is active.  So I think we do want to diagnose
>>> the new-expression above; the user should write new (&u) if that's what
>>> they mean.
>>
>> Okay. I changed that in the latest patch.
>>
>>>> Adjust is negative when the offset to a buffer of known size is
>>>> negative. For example:
>>>>
>>>>      char buf [sizeof (int)];
>>>>      new (&buf [1] - 1) int;
>>>
>>> OK, so because we're looking at the expression from the outside in, we
>>> first see the subtraction and adjust becomes -1, then we see the
>>> array_ref and adjust returns to 0.  We still don't have a negative
>>> adjust by the time we get to the quoted if/else.
>>
>> I think I see what you mean. I've changed the type of the variables
>> and the computation to unsigned. That made it possible to eliminate
>> the final else and do some other cleanup. Attached is an updated
>> patch.
>
> Hmm, I was suggesting that bytes_avail change to unsigned, but I don't
> think adjust should change; I believe that 0u - 1u is undefined due to
> overflow even though (-1u) and (unsigned)-1 are well defined.  Sorry for
> the muddled messages.  I think let's leave adjust signed and assert that
> it ends up non-negative.

No problem.

Unsigned ints wrap around and don't overflow so the subtraction
is well defined (0u - 1u is equal UINT_MAX).

FWIW, I had the assert there for sanity testing when you first
mentioned it to convince myself there really was no way for it
become negative. A bootstrap went fine with it but it still made
me just a teeny bit uneasy. I would hate for the code to change
in the future and for the assert to then fire after it's released.

In any case, I defer to your better judgment. Please let me know
if you would still like to go with signed + assert.

Thanks
Martin

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 15:13               ` Martin Sebor
@ 2015-11-05 15:27                 ` Jason Merrill
  2015-11-05 16:52                   ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2015-11-05 15:27 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 11/05/2015 10:12 AM, Martin Sebor wrote:
> On 11/04/2015 09:27 PM, Jason Merrill wrote:
>> On 11/04/2015 07:15 PM, Martin Sebor wrote:
>>>> There was a lot of discussion of C++ aliasing rules at the recent
>>>> meeting; we really seem to be moving in the direction of being stricter
>>>> about which union member is active.  So I think we do want to diagnose
>>>> the new-expression above; the user should write new (&u) if that's what
>>>> they mean.
>>>
>>> Okay. I changed that in the latest patch.
>>>
>>>>> Adjust is negative when the offset to a buffer of known size is
>>>>> negative. For example:
>>>>>
>>>>>      char buf [sizeof (int)];
>>>>>      new (&buf [1] - 1) int;
>>>>
>>>> OK, so because we're looking at the expression from the outside in, we
>>>> first see the subtraction and adjust becomes -1, then we see the
>>>> array_ref and adjust returns to 0.  We still don't have a negative
>>>> adjust by the time we get to the quoted if/else.
>>>
>>> I think I see what you mean. I've changed the type of the variables
>>> and the computation to unsigned. That made it possible to eliminate
>>> the final else and do some other cleanup. Attached is an updated
>>> patch.
>>
>> Hmm, I was suggesting that bytes_avail change to unsigned, but I don't
>> think adjust should change; I believe that 0u - 1u is undefined due to
>> overflow even though (-1u) and (unsigned)-1 are well defined.  Sorry for
>> the muddled messages.  I think let's leave adjust signed and assert that
>> it ends up non-negative.
>
> No problem.
>
> Unsigned ints wrap around and don't overflow so the subtraction
> is well defined (0u - 1u is equal UINT_MAX).

I thought I had remembered that, but couldn't find anything in the 
standard to back it up.  Now I see that it's in 3.9.1 rather than clause 5.

> FWIW, I had the assert there for sanity testing when you first
> mentioned it to convince myself there really was no way for it
> become negative. A bootstrap went fine with it but it still made
> me just a teeny bit uneasy. I would hate for the code to change
> in the future and for the assert to then fire after it's released.

> In any case, I defer to your better judgment. Please let me know
> if you would still like to go with signed + assert.

If we use gcc_checking_assert it won't fire in release builds; let's go 
with that.

Jason

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 15:27                 ` Jason Merrill
@ 2015-11-05 16:52                   ` Martin Sebor
  2015-11-05 18:13                     ` Jason Merrill
                                       ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: Martin Sebor @ 2015-11-05 16:52 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

> If we use gcc_checking_assert it won't fire in release builds; let's go
> with that.

Okay. Attached is an updated patch with that change.

Martin


[-- Attachment #2: gcc-67942-placement-new-overflow.patch --]
[-- Type: text/x-patch, Size: 30151 bytes --]

gcc ChangeLog
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.

gcc/c-family ChangeLog
2015-11-05  Martin Sebor  <msebor@redhat.com>

	PR c++/67942
        * c.opt (-Wplacement-new): New option.

gcc/cp ChangeLog
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.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 47ba070..5e9d7a3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -760,6 +760,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
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ed8f6c..3a9c59d 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2269,6 +2269,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
@@ -2518,6 +2713,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))
     {
@@ -2566,11 +2763,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.  */
@@ -2645,20 +2844,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);
 	}
     }

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 547ee2d..337639e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -271,7 +271,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
@@ -4789,6 +4799,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
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
index 0000000..a8a2a68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
@@ -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];
+    }
+}

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 16:52                   ` Martin Sebor
@ 2015-11-05 18:13                     ` Jason Merrill
  2015-11-05 23:41                       ` Martin Sebor
  2015-11-06 12:50                     ` Andreas Schwab
  2015-11-06 12:55                     ` Rainer Orth
  2 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2015-11-05 18:13 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

OK, thanks.

Jason

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 18:13                     ` Jason Merrill
@ 2015-11-05 23:41                       ` Martin Sebor
  2015-11-06  1:10                         ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2015-11-05 23:41 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

On 11/05/2015 11:13 AM, Jason Merrill wrote:
> OK, thanks.

I'm afraid the last patch that I just committed breaks libstdc++
bootstrap with the following error:

/home/msebor/scm/fsf/gcc-svn/libstdc++-v3/src/c++98/ios_init.cc: In 
static member function ‘static bool std::ios_base::sync_with_stdio(bool)’:
/home/msebor/scm/fsf/gcc-svn/libstdc++-v3/src/c++98/ios_init.cc:181:59: 
internal compiler error: in tree_to_shwi, at tree.c:7297
   new (&buf_cout) stdio_filebuf<char>(stdout, ios_base::out);

I thought it had something to do with the recent changes made to
the patch but it looks like it's a problem that's been there all
along: the patch assumes DECL_SIZE_UNIT (oper) is non-null but
it turns out that's not a safe assumption. I don't know why my
testing didn't expose it before, despite running make bootstrap
and make check.

The following patch (only the first bit is important) fixes the
ICE. I will commit it as soon as I'm done testing it.

Sorry about the breakage.

Martin

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 7600363..ca7de56 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2384,12 +2384,13 @@ warn_placement_new_too_small (tree type, tree 
nelts, tree size, tree oper)
        /* 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)
+      if ((TREE_CODE (oper) == VAR_DECL || use_obj_size)
+         && (DECL_SIZE_UNIT (oper)))
         {
           /* 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));
+         bytes_avail = tree_to_uhwi (DECL_SIZE_UNIT (oper));
           exact_size = !use_obj_size;
         }
        else

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 23:41                       ` Martin Sebor
@ 2015-11-06  1:10                         ` Martin Sebor
  0 siblings, 0 replies; 18+ messages in thread
From: Martin Sebor @ 2015-11-06  1:10 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

On 11/05/2015 04:41 PM, Martin Sebor wrote:
> On 11/05/2015 11:13 AM, Jason Merrill wrote:
>> OK, thanks.
>
> I'm afraid the last patch that I just committed breaks libstdc++
> bootstrap with the following error:

I committed a slightly more robust fix that avoids assuming that
either the size of the object or its type is known.  Those were
both bugs in the patch.

What seems unexpected to me that given the following declarations,
DECL_SIZE_UNIT(s) is null but TYPE_SIZE_UNIT (TREE_TYPE (s)) is
not.  The code is derived from the libstdc++ translation unit that
caused the ICE.  Is it because GCC conservatively assumes that
S<char> could be explicitly specialized in another translation
unit?  If so, then I would expect TYPE_SIZE_UNIT (TREE_TYPE (s))
to also be null.  What am I missing?

   template <class> struct S { int i; };

   extern struct S<char> s;

Martin

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 16:52                   ` Martin Sebor
  2015-11-05 18:13                     ` Jason Merrill
@ 2015-11-06 12:50                     ` Andreas Schwab
  2015-11-08 18:10                       ` Martin Sebor
  2015-11-06 12:55                     ` Rainer Orth
  2 siblings, 1 reply; 18+ messages in thread
From: Andreas Schwab @ 2015-11-06 12:50 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, Gcc Patch List

I see this failure on m68k:

FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++11 (test for excess errors)
Excess errors:
/daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:189:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
/daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:191:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
/daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:194:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
/daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:198:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]

That appears to be a 32-bit problem, the test also fails here on x86-64
with -m32 <http://gcc.gnu.org/ml/gcc-testresults/2015-11/msg00522.html>
or here on powerpc
<http://gcc.gnu.org/ml/gcc-testresults/2015-11/msg00520.html>

Andreas.

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

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-05 16:52                   ` Martin Sebor
  2015-11-05 18:13                     ` Jason Merrill
  2015-11-06 12:50                     ` Andreas Schwab
@ 2015-11-06 12:55                     ` Rainer Orth
  2015-11-06 15:28                       ` Martin Sebor
  2 siblings, 1 reply; 18+ messages in thread
From: Rainer Orth @ 2015-11-06 12:55 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, Gcc Patch List

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

Martin Sebor <msebor@gmail.com> writes:

>> If we use gcc_checking_assert it won't fire in release builds; let's go
>> with that.
>
> Okay. Attached is an updated patch with that change.

Unfortunately, this breaks i386-pc-solaris2.10 bootstrap:

/vol/gcc/src/hg/trunk/local/gcc/cp/init.c: In function 'void warn_placement_new_too_small(tree, tree, tree, tree)':
/vol/gcc/src/hg/trunk/local/gcc/cp/init.c:2454:17: error: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'long long unsigned int' [-Werror=format=]
      bytes_avail);
                 ^

Printing an unsigned HOST_WIDE_INT with %lu in one case, but %wu in the
other seems like a simple typo, so the following fixes bootstrap for me:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: cp-init.patch --]
[-- Type: text/x-patch, Size: 505 bytes --]

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2447,7 +2447,7 @@ warn_placement_new_too_small (tree 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 "
+			  "%<%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),

[-- Attachment #3: Type: text/plain, Size: 153 bytes --]


	Rainer


-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-06 12:55                     ` Rainer Orth
@ 2015-11-06 15:28                       ` Martin Sebor
  0 siblings, 0 replies; 18+ messages in thread
From: Martin Sebor @ 2015-11-06 15:28 UTC (permalink / raw)
  To: Rainer Orth; +Cc: Jason Merrill, Gcc Patch List, ktkachov

On 11/06/2015 05:55 AM, Rainer Orth wrote:
> Martin Sebor <msebor@gmail.com> writes:
>
>>> If we use gcc_checking_assert it won't fire in release builds; let's go
>>> with that.
>>
>> Okay. Attached is an updated patch with that change.
>
> Unfortunately, this breaks i386-pc-solaris2.10 bootstrap:
>
> /vol/gcc/src/hg/trunk/local/gcc/cp/init.c: In function 'void warn_placement_new_too_small(tree, tree, tree, tree)':
> /vol/gcc/src/hg/trunk/local/gcc/cp/init.c:2454:17: error: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'long long unsigned int' [-Werror=format=]
>        bytes_avail);
>                   ^
>
> Printing an unsigned HOST_WIDE_INT with %lu in one case, but %wu in the
> other seems like a simple typo, so the following fixes bootstrap for me:

Yes, that was a typo. Sorry about that and thanks to ktkachov for
committing the fix!

Martin

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

* Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow
  2015-11-06 12:50                     ` Andreas Schwab
@ 2015-11-08 18:10                       ` Martin Sebor
  0 siblings, 0 replies; 18+ messages in thread
From: Martin Sebor @ 2015-11-08 18:10 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: Jason Merrill, Gcc Patch List

On 11/06/2015 05:50 AM, Andreas Schwab wrote:
> I see this failure on m68k:
>
> FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++11 (test for excess errors)
> Excess errors:
> /daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:189:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
> /daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:191:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
> /daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:194:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
> /daten/aranym/gcc/gcc-20151106/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C:198:19: warning: placement new constructing an object of type 'int' and size '4' in a region of type 'char [4]' and size '0' [-Wplacement-new]
>
> That appears to be a 32-bit problem, the test also fails here on x86-64
> with -m32 <http://gcc.gnu.org/ml/gcc-testresults/2015-11/msg00522.html>
> or here on powerpc
> <http://gcc.gnu.org/ml/gcc-testresults/2015-11/msg00520.html>

This should be fixed now via r229959 (tested on x86_64 with -m32).

The problem was caused by assuming that the POINTER_PLUS_EXPR offset
which is stored as sizetype, an unsigned 32-bit type in ILP32, can
be "extracted" as an unsigned HOST_WIDE_INT (a 64-bit type when
the host compiler is LP64), and converted to signed to obtain the
original negative offset.

Martin

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

end of thread, other threads:[~2015-11-08 18:10 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-13 17:23 [PATCH] c++/67942 - diagnose placement new buffer overflow Martin Sebor
2015-10-21  0:03 ` [PING] " Martin Sebor
2015-10-27  2:07   ` [PING 2] " Martin Sebor
2015-11-03  2:41     ` Jason Merrill
2015-11-04 17:12       ` Martin Sebor
2015-11-04 19:12         ` Jason Merrill
2015-11-05  0:15           ` Martin Sebor
2015-11-05  4:27             ` Jason Merrill
2015-11-05 15:13               ` Martin Sebor
2015-11-05 15:27                 ` Jason Merrill
2015-11-05 16:52                   ` Martin Sebor
2015-11-05 18:13                     ` Jason Merrill
2015-11-05 23:41                       ` Martin Sebor
2015-11-06  1:10                         ` Martin Sebor
2015-11-06 12:50                     ` Andreas Schwab
2015-11-08 18:10                       ` Martin Sebor
2015-11-06 12:55                     ` Rainer Orth
2015-11-06 15:28                       ` 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).