From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 60858 invoked by alias); 20 Oct 2015 23:57:14 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 60847 invoked by uid 89); 20 Oct 2015 23:57:13 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.1 required=5.0 tests=AWL,BAYES_50,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-qk0-f175.google.com Received: from mail-qk0-f175.google.com (HELO mail-qk0-f175.google.com) (209.85.220.175) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Tue, 20 Oct 2015 23:57:09 +0000 Received: by qkca6 with SMTP id a6so15399875qkc.3 for ; Tue, 20 Oct 2015 16:57:07 -0700 (PDT) X-Received: by 10.55.197.77 with SMTP id p74mr7380163qki.89.1445385426973; Tue, 20 Oct 2015 16:57:06 -0700 (PDT) Received: from [192.168.0.26] (70-59-0-242.hlrn.qwest.net. [70.59.0.242]) by smtp.gmail.com with ESMTPSA id f129sm2212444qhe.45.2015.10.20.16.57.05 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 20 Oct 2015 16:57:06 -0700 (PDT) Message-ID: <5626D4D0.4010603@gmail.com> Date: Wed, 21 Oct 2015 00:03:00 -0000 From: Martin Sebor User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.4.0 MIME-Version: 1.0 To: Gcc Patch List Subject: [PING] [PATCH] c++/67942 - diagnose placement new buffer overflow References: <561D3DEC.5090209@gmail.com> In-Reply-To: <561D3DEC.5090209@gmail.com> Content-Type: multipart/mixed; boundary="------------060704020107080407030000" X-IsSubscribed: yes X-SW-Source: 2015-10/txt/msg02002.txt.bz2 This is a multi-part message in MIME format. --------------060704020107080407030000 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1298 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 > --------------060704020107080407030000 Content-Type: text/x-patch; name="gcc-67942-placement-new-overflow.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gcc-67942-placement-new-overflow.patch" Content-length: 30001 gcc ChangeLog 2015-10-20 Martin Sebor 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 PR c++/67942 * c.opt (-Wplacement-new): New option. gcc/cp ChangeLog 2015-10-20 Martin Sebor 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(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 **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 **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 **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 **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(&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" } + 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]; + } +} --------------060704020107080407030000--