This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PING 2] [PATCH] c++/67942 - diagnose placement new buffer overflow


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
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];
+    }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]