From e2f5cc96e5c4abde5879269220404fdd3e6efca2 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Thu, 5 Nov 2015 21:42:10 +0000 Subject: [PATCH] PR c++/67942 - diagnose placement new buffer overflow gcc/ * invoke.texi (-Wplacement-new): Document new option. * gcc/testsuite/g++.dg/warn/Wplacement-new-size.C: New test. gcc/c-family/ * c.opt (-Wplacement-new): New option. gcc/cp/ * cp/init.c (warn_placement_new_too_small): New function. (build_new_1): Call it. gcc/testsuite/ * g++.dg/warn/Wplacement-new-size.C: New test. From-SVN: r229827 --- gcc/ChangeLog | 6 + gcc/c-family/ChangeLog | 5 + gcc/c-family/c.opt | 4 + gcc/cp/ChangeLog | 6 + gcc/cp/init.c | 223 +++++++++- gcc/doc/invoke.texi | 9 +- gcc/testsuite/ChangeLog | 5 + .../g++.dg/warn/Wplacement-new-size.C | 410 ++++++++++++++++++ 8 files changed, 660 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wplacement-new-size.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0dd763afc20..94e597e025a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2015-11-05 Martin Sebor + + PR c++/67942 + * invoke.texi (-Wplacement-new): Document new option. + * gcc/testsuite/g++.dg/warn/Wplacement-new-size.C: New test. + 2015-11-05 Alan Lawrence PR tree-optimization/65963 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index ffb97c94dfd..c21f50410fa 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2015-11-05 Martin Sebor + + PR c++/67942 + * c.opt (-Wplacement-new): New option. + 2015-11-05 Jakub Jelinek * c-common.h (c_finish_omp_atomic): Add TEST argument. diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index e573254686e..5f38018d35e 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -776,6 +776,10 @@ Wprotocol ObjC ObjC++ Var(warn_protocol) Init(1) Warning Warn if inherited methods are unimplemented. +Wplacement-new +C++ Var(warn_placement_new) Init(1) Warning +Warn for placement new expressions with undefined behavior + Wredundant-decls C ObjC C++ ObjC++ Var(warn_redundant_decls) Warning Warn about multiple declarations of the same object. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 57de3e6b639..b84778f3242 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,9 @@ +2015-11-05 Martin Sebor + + PR c++/67942 + * cp/init.c (warn_placement_new_too_small): New function. + (build_new_1): Call it. + 2015-11-05 Paolo Carlini PR c++/67846 diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 035ec2921be..7600363447d 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -2276,6 +2276,201 @@ throw_bad_array_new_length (void) return build_cxx_call (fn, 0, NULL, tf_warning_or_error); } +/* Attempt to verify that the argument, OPER, of a placement new expression + refers to an object sufficiently large for an object of TYPE or an array + of NELTS of such objects when NELTS is non-null, and issue a warning when + it does not. SIZE specifies the size needed to construct the object or + array and captures the result of NELTS * sizeof (TYPE). (SIZE could be + greater when the array under construction requires a cookie to store + NELTS. GCC's placement new expression stores the cookie when invoking + a user-defined placement new operator function but not the default one. + Placement new expressions with user-defined placement new operator are + not diagnosed since we don't know how they use the buffer (this could + be a future extension). */ +static void +warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper) +{ + location_t loc = EXPR_LOC_OR_LOC (oper, input_location); + + /* The number of bytes to add to or subtract from the size of the provided + buffer based on an offset into an array or an array element reference. + Although intermediate results may be negative (as in a[3] - 2) the final + result cannot be. */ + HOST_WIDE_INT adjust = 0; + /* True when the size of the entire destination object should be used + to compute the possibly optimistic estimate of the available space. */ + bool use_obj_size = false; + /* True when the reference to the destination buffer is an ADDR_EXPR. */ + bool addr_expr = false; + + STRIP_NOPS (oper); + + /* Using a function argument or a (non-array) variable as an argument + to placement new is not checked since it's unknown what it might + point to. */ + if (TREE_CODE (oper) == PARM_DECL + || TREE_CODE (oper) == VAR_DECL + || TREE_CODE (oper) == COMPONENT_REF) + return; + + /* Evaluate any constant expressions. */ + size = fold_non_dependent_expr (size); + + /* Handle the common case of array + offset expression when the offset + is a constant. */ + if (TREE_CODE (oper) == POINTER_PLUS_EXPR) + { + /* If the offset is comple-time constant, use it to compute a more + accurate estimate of the size of the buffer. Otherwise, use + the size of the entire array as an optimistic estimate (this + may lead to false negatives). */ + const_tree adj = TREE_OPERAND (oper, 1); + if (CONSTANT_CLASS_P (adj)) + adjust += tree_to_uhwi (adj); + else + use_obj_size = true; + + oper = TREE_OPERAND (oper, 0); + + STRIP_NOPS (oper); + } + + if (TREE_CODE (oper) == TARGET_EXPR) + oper = TREE_OPERAND (oper, 1); + else if (TREE_CODE (oper) == ADDR_EXPR) + { + addr_expr = true; + oper = TREE_OPERAND (oper, 0); + } + + STRIP_NOPS (oper); + + if (TREE_CODE (oper) == ARRAY_REF) + { + /* Similar to the offset computed above, see if the array index + is a compile-time constant. If so, and unless the offset was + not a compile-time constant, use the index to determine the + size of the buffer. Otherwise, use the entire array as + an optimistic estimate of the size. */ + const_tree adj = TREE_OPERAND (oper, 1); + if (!use_obj_size && CONSTANT_CLASS_P (adj)) + adjust += tree_to_shwi (adj); + else + { + use_obj_size = true; + adjust = 0; + } + + oper = TREE_OPERAND (oper, 0); + } + + /* Descend into a struct or union to find the member whose address + is being used as the agument. */ + while (TREE_CODE (oper) == COMPONENT_REF) + oper = TREE_OPERAND (oper, 1); + + if ((addr_expr || !POINTER_TYPE_P (TREE_TYPE (oper))) + && (TREE_CODE (oper) == VAR_DECL + || TREE_CODE (oper) == FIELD_DECL + || TREE_CODE (oper) == PARM_DECL)) + { + /* A possibly optimistic estimate of the number of bytes available + in the destination buffer. */ + unsigned HOST_WIDE_INT bytes_avail; + /* True when the estimate above is in fact the exact size + of the destination buffer rather than an estimate. */ + bool exact_size = true; + + /* Treat members of unions and members of structs uniformly, even + though the size of a member of a union may be viewed as extending + to the end of the union itself (it is by __builtin_object_size). */ + if (TREE_CODE (oper) == VAR_DECL || use_obj_size) + { + /* Use the size of the entire array object when the expression + refers to a variable or its size depends on an expression + that's not a compile-time constant. */ + bytes_avail = tree_to_shwi (DECL_SIZE_UNIT (oper)); + exact_size = !use_obj_size; + } + else + { + /* Use the size of the type of the destination buffer object + as the optimistic estimate of the available space in it. */ + bytes_avail = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (oper))); + } + + /* Avoid diagnosing flexible array members (accepted as an extension + and diagnosed with -Wpedantic). + Constructing objects that appear to overflow the C99 equivalent of + flexible array members (i.e., array members of size zero or one) + are diagnosed in C++ since their declaration cannot be diagnosed. */ + if (bytes_avail == 0 && TREE_CODE (TREE_TYPE (oper)) == ARRAY_TYPE) + return; + + /* The size of the buffer can only be adjusted down but not up. */ + gcc_checking_assert (0 <= adjust); + + /* Reduce the size of the buffer by the adjustment computed above + from the offset and/or the index into the array. */ + if (bytes_avail < static_cast(adjust)) + bytes_avail = 0; + else + bytes_avail -= adjust; + + /* The minimum amount of space needed for the allocation. This + is an optimistic estimate that makes it possible to detect + placement new invocation for some undersize buffers but not + others. */ + unsigned HOST_WIDE_INT bytes_need; + + if (CONSTANT_CLASS_P (size)) + bytes_need = tree_to_uhwi (size); + else if (nelts && CONSTANT_CLASS_P (nelts)) + bytes_need = tree_to_uhwi (nelts) + * tree_to_uhwi (TYPE_SIZE_UNIT (type)); + else + bytes_need = tree_to_uhwi (TYPE_SIZE_UNIT (type)); + + if (bytes_avail < bytes_need) + { + if (nelts) + if (CONSTANT_CLASS_P (nelts)) + warning_at (loc, OPT_Wplacement_new, + exact_size ? + "placement new constructing an object of type " + "%<%T [%wu]%> and size %qwu in a region of type %qT " + "and size %qwi" + : "placement new constructing an object of type " + "%<%T [%lu]%> and size %qwu in a region of type %qT " + "and size at most %qwu", + type, tree_to_uhwi (nelts), bytes_need, + TREE_TYPE (oper), + bytes_avail); + else + warning_at (loc, OPT_Wplacement_new, + exact_size ? + "placement new constructing an array of objects " + "of type %qT and size %qwu in a region of type %qT " + "and size %qwi" + : "placement new constructing an array of objects " + "of type %qT and size %qwu in a region of type %qT " + "and size at most %qwu", + type, bytes_need, TREE_TYPE (oper), + bytes_avail); + else + warning_at (loc, OPT_Wplacement_new, + exact_size ? + "placement new constructing an object of type %qT " + "and size %qwu in a region of type %qT and size %qwi" + : "placement new constructing an object of type %qT" + "and size %qwu in a region of type %qT and size " + "at most %qwu", + type, bytes_need, TREE_TYPE (oper), + bytes_avail); + } + } +} + /* Generate code for a new-expression, including calling the "operator new" function, initializing the object, and, if an exception occurs during construction, cleaning up. The arguments are as for @@ -2525,6 +2720,8 @@ build_new_1 (vec **placement, tree type, tree nelts, && (TYPE_PTR_P (TREE_TYPE ((**placement)[0])))) placement_first = (**placement)[0]; + bool member_new_p = false; + /* Allocate the object. */ if (vec_safe_is_empty (*placement) && TYPE_FOR_JAVA (elt_type)) { @@ -2573,11 +2770,13 @@ build_new_1 (vec **placement, tree type, tree nelts, fnname = ansi_opname (array_p ? VEC_NEW_EXPR : NEW_EXPR); - if (!globally_qualified_p + member_new_p = !globally_qualified_p && CLASS_TYPE_P (elt_type) && (array_p ? TYPE_HAS_ARRAY_NEW_OPERATOR (elt_type) - : TYPE_HAS_NEW_OPERATOR (elt_type))) + : TYPE_HAS_NEW_OPERATOR (elt_type)); + + if (member_new_p) { /* Use a class-specific operator new. */ /* If a cookie is required, add some extra space. */ @@ -2652,20 +2851,30 @@ build_new_1 (vec **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 4fc7d887a72..587e30e613d 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -272,7 +272,7 @@ Objective-C and Objective-C++ Dialects}. -Woverride-init-side-effects @gol -Woverlength-strings -Wpacked -Wpacked-bitfield-compat -Wpadded @gol -Wparentheses -Wpedantic-ms-format -Wno-pedantic-ms-format @gol --Wpointer-arith -Wno-pointer-to-int-cast @gol +-Wplacement-new -Wpointer-arith -Wno-pointer-to-int-cast @gol -Wredundant-decls -Wno-return-local-addr @gol -Wreturn-type -Wsequence-point -Wshadow -Wno-shadow-ivar @gol -Wshift-overflow -Wshift-overflow=@var{n} @gol @@ -4870,6 +4870,13 @@ disables the warnings about non-ISO @code{printf} / @code{scanf} format width specifiers @code{I32}, @code{I64}, and @code{I} used on Windows targets, which depend on the MS runtime. +@item -Wplacement-new +@opindex Wplacement-new +@opindex Wno-placement-new +Warn about placement new expressions with undefined behavior, such as +constructing an object in a buffer that is smaller than the type of +the object. + @item -Wpointer-arith @opindex Wpointer-arith @opindex Wno-pointer-arith diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d41124e91cf..c0a0d78ccc6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2015-11-05 Martin Sebor + + PR c++/67942 + * g++.dg/warn/Wplacement-new-size.C: New test. + 2015-11-05 Alan Lawrence * gcc.dg/pr68112.c: New. diff --git a/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C new file mode 100644 index 00000000000..a8a2a686e55 --- /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(&c)) char [4]; // { dg-warning "placement" } + new (reinterpret_cast(&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]; + } +} -- 2.30.2