diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index bb6aa86b4e73..97371919b028 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -2,6 +2,7 @@ import semmle.code.cpp.Element private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass private import semmle.code.cpp.internal.AddressConstantExpression +private import semmle.code.cpp.models.implementations.Allocation /** * A C/C++ expression. @@ -804,8 +805,10 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr { * call the constructor of `T` but will not allocate memory. */ Expr getPlacementPointer() { - isStandardPlacementNewAllocator(this.getAllocator()) and - result = this.getAllocatorCall().getArgument(1) + result = + this + .getAllocatorCall() + .getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument()) } } @@ -1194,12 +1197,6 @@ private predicate convparents(Expr child, int idx, Element parent) { ) } -private predicate isStandardPlacementNewAllocator(Function operatorNew) { - operatorNew.getName().matches("operator new%") and - operatorNew.getNumberOfParameters() = 2 and - operatorNew.getParameter(1).getType() instanceof VoidPointerType -} - // Pulled out for performance. See QL-796. private predicate hasNoConversions(Expr e) { not e.hasConversion() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll index fb7dc1738128..f2d098afbcc9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -343,6 +343,11 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects { override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { expr.getTarget() instanceof AllocationFunction and + not exists(NewOrNewArrayExpr newExpr | + // we synthesize allocator calls for `new` and `new[]`, so don't add instructions to + // the existing allocator call when it exists. + newExpr.getAllocatorCall() = expr + ) and opcode instanceof Opcode::InitializeDynamicAllocation and tag = OnlyInstructionTag() and type = getUnknownType() @@ -358,6 +363,11 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects { tag = OnlyInstructionTag() and kind = gotoEdge() and expr.getTarget() instanceof AllocationFunction and + not exists(NewOrNewArrayExpr newExpr | + // we synthesize allocator calls for `new` and `new[]`, so don't add instructions to + // the existing allocator call when it exists. + newExpr.getAllocatorCall() = expr + ) and if exists(getChild(0)) then result = getChild(0).getFirstInstruction() else result = getParent().getChildSuccessor(this) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll index e2f34fe03ccf..c6766983889c 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll @@ -215,6 +215,40 @@ class SizelessAllocationFunction extends AllocationFunction { } } +/** + * An `operator new` or `operator new[]` function that may be associated with `new` or + * `new[]` expressions. Note that `new` and `new[]` are not function calls, but these + * functions may also be called directly. + */ +class OperatorNewAllocationFunction extends AllocationFunction { + OperatorNewAllocationFunction() { + exists(string name | + hasGlobalName(name) and + ( + // operator new(bytes, ...) + name = "operator new" + or + // operator new[](bytes, ...) + name = "operator new[]" + ) + ) + } + + override int getSizeArg() { result = 0 } + + override predicate requiresDealloc() { not exists(getPlacementArgument()) } + + /** + * Gets the position of the placement pointer if this is a placement + * `operator new` function. + */ + int getPlacementArgument() { + getNumberOfParameters() = 2 and + getParameter(1).getType() instanceof VoidPointerType and + result = 1 + } +} + /** * An allocation expression that is a function call, such as call to `malloc`. */ @@ -227,7 +261,9 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall { not ( exists(target.getReallocPtrArg()) and getArgument(target.getSizeArg()).getValue().toInt() = 0 - ) + ) and + // these are modelled directly (and more accurately), avoid duplication + not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this) } override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll index 83309855e189..d2e4951e436a 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll @@ -79,6 +79,28 @@ class StandardDeallocationFunction extends DeallocationFunction { override int getFreedArg() { result = freedArg } } +/** + * An `operator delete` or `operator delete[]` function that may be associated + * with `delete` or `delete[]` expressions. Note that `delete` and `delete[]` + * are not function calls, but these functions may also be called directly. + */ +class OperatorDeleteDeallocationFunction extends DeallocationFunction { + OperatorDeleteDeallocationFunction() { + exists(string name | + hasGlobalName(name) and + ( + // operator delete(pointer, ...) + name = "operator delete" + or + // operator delete[](pointer, ...) + name = "operator delete[]" + ) + ) + } + + override int getFreedArg() { result = 0 } +} + /** * An deallocation expression that is a function call, such as call to `free`. */ diff --git a/cpp/ql/test/library-tests/allocators/allocators.cpp b/cpp/ql/test/library-tests/allocators/allocators.cpp index 6571245f5fc3..92241b9c9e3f 100644 --- a/cpp/ql/test/library-tests/allocators/allocators.cpp +++ b/cpp/ql/test/library-tests/allocators/allocators.cpp @@ -143,3 +143,9 @@ void multidimensionalNew(int x, int y) { auto p2 = new char[20][20]; auto p3 = new char[x][30][30]; } + +void directOperatorCall() { + void *ptr; + ptr = operator new(sizeof(int)); + operator delete(ptr); +} diff --git a/cpp/ql/test/library-tests/allocators/allocators.expected b/cpp/ql/test/library-tests/allocators/allocators.expected index ee67a413692e..2f9b4413e8cc 100644 --- a/cpp/ql/test/library-tests/allocators/allocators.expected +++ b/cpp/ql/test/library-tests/allocators/allocators.expected @@ -1,28 +1,28 @@ newExprs -| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | -| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | | -| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | -| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | | -| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | | -| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned | -| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned | -| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | | -| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned | -| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | | -| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | | +| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | | +| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | | | +| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | | +| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | | | +| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | | | +| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned | | +| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned | | +| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | | | +| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned | | +| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | | & ... | +| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | | | newArrayExprs -| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n | -| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n | -| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n | -| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n | -| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | | -| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n | -| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | | -| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | | -| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | | -| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x | -| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | | -| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x | +| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n | | +| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n | | +| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n | | +| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n | | +| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | | | +| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n | | +| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | | | +| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | | buf | +| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | | | +| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x | | +| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | | | +| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x | | newExprDeallocators | allocators.cpp:52:3:52:14 | new | String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized | | allocators.cpp:53:3:53:27 | new | String | operator delete(void *, float) -> void | 8 | 8 | | @@ -46,3 +46,65 @@ deleteArrayExprs | allocators.cpp:81:3:81:45 | delete[] | Overaligned | operator delete[](void *, unsigned long, align_val_t) -> void | 256 | 128 | sized aligned | | allocators.cpp:82:3:82:49 | delete[] | PolymorphicBase | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized | | allocators.cpp:83:3:83:23 | delete[] | int | operator delete[](void *, unsigned long) -> void | 4 | 4 | sized | +allocationFunctions +| allocators.cpp:7:7:7:18 | operator new | getSizeArg = 0, requiresDealloc | +| allocators.cpp:8:7:8:20 | operator new[] | getSizeArg = 0, requiresDealloc | +| allocators.cpp:9:7:9:18 | operator new | getSizeArg = 0, requiresDealloc | +| allocators.cpp:10:7:10:20 | operator new[] | getSizeArg = 0, requiresDealloc | +| allocators.cpp:121:7:121:18 | operator new | getPlacementArgument = 1, getSizeArg = 0 | +| allocators.cpp:122:7:122:20 | operator new[] | getPlacementArgument = 1, getSizeArg = 0 | +| allocators.cpp:123:7:123:18 | operator new | getSizeArg = 0, requiresDealloc | +| allocators.cpp:124:7:124:20 | operator new[] | getSizeArg = 0, requiresDealloc | +| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc | +| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc | +| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc | +| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc | +allocationExprs +| allocators.cpp:49:3:49:9 | new | getSizeBytes = 4, requiresDealloc | +| allocators.cpp:50:3:50:15 | new | getSizeBytes = 4, requiresDealloc | +| allocators.cpp:51:3:51:11 | new | getSizeBytes = 4, requiresDealloc | +| allocators.cpp:52:3:52:14 | new | getSizeBytes = 8, requiresDealloc | +| allocators.cpp:53:3:53:27 | new | getSizeBytes = 8, requiresDealloc | +| allocators.cpp:54:3:54:17 | new | getSizeBytes = 256, requiresDealloc | +| allocators.cpp:55:3:55:25 | new | getSizeBytes = 256, requiresDealloc | +| allocators.cpp:68:3:68:12 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc | +| allocators.cpp:69:3:69:18 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc | +| allocators.cpp:70:3:70:15 | new[] | getSizeExpr = n, getSizeMult = 8, requiresDealloc | +| allocators.cpp:71:3:71:20 | new[] | getSizeExpr = n, getSizeMult = 256, requiresDealloc | +| allocators.cpp:72:3:72:16 | new[] | getSizeBytes = 80, requiresDealloc | +| allocators.cpp:107:3:107:18 | new | getSizeBytes = 1, requiresDealloc | +| allocators.cpp:108:3:108:19 | new[] | getSizeExpr = n, getSizeMult = 1, requiresDealloc | +| allocators.cpp:109:3:109:35 | new | getSizeBytes = 128, requiresDealloc | +| allocators.cpp:110:3:110:37 | new[] | getSizeBytes = 1280, requiresDealloc | +| allocators.cpp:129:3:129:21 | new | getSizeBytes = 4 | +| allocators.cpp:132:3:132:17 | new[] | getSizeBytes = 4 | +| allocators.cpp:135:3:135:26 | new | getSizeBytes = 4, requiresDealloc | +| allocators.cpp:136:3:136:26 | new[] | getSizeBytes = 8, requiresDealloc | +| allocators.cpp:142:13:142:27 | new[] | getSizeExpr = x, getSizeMult = 10, requiresDealloc | +| allocators.cpp:143:13:143:28 | new[] | getSizeBytes = 400, requiresDealloc | +| allocators.cpp:144:13:144:31 | new[] | getSizeExpr = x, getSizeMult = 900, requiresDealloc | +| allocators.cpp:149:8:149:19 | call to operator new | getSizeBytes = 4, getSizeExpr = sizeof(int), getSizeMult = 1, requiresDealloc | +deallocationFunctions +| allocators.cpp:11:6:11:20 | operator delete | getFreedArg = 0 | +| allocators.cpp:12:6:12:22 | operator delete[] | getFreedArg = 0 | +| allocators.cpp:13:6:13:20 | operator delete | getFreedArg = 0 | +| allocators.cpp:14:6:14:22 | operator delete[] | getFreedArg = 0 | +| file://:0:0:0:0 | operator delete | getFreedArg = 0 | +| file://:0:0:0:0 | operator delete | getFreedArg = 0 | +| file://:0:0:0:0 | operator delete | getFreedArg = 0 | +| file://:0:0:0:0 | operator delete[] | getFreedArg = 0 | +| file://:0:0:0:0 | operator delete[] | getFreedArg = 0 | +deallocationExprs +| allocators.cpp:59:3:59:35 | delete | getFreedExpr = 0 | +| allocators.cpp:60:3:60:38 | delete | getFreedExpr = 0 | +| allocators.cpp:61:3:61:44 | delete | getFreedExpr = 0 | +| allocators.cpp:62:3:62:43 | delete | getFreedExpr = 0 | +| allocators.cpp:63:3:63:47 | delete | getFreedExpr = 0 | +| allocators.cpp:64:3:64:44 | delete | getFreedExpr = 0 | +| allocators.cpp:78:3:78:37 | delete[] | getFreedExpr = 0 | +| allocators.cpp:79:3:79:40 | delete[] | getFreedExpr = 0 | +| allocators.cpp:80:3:80:46 | delete[] | getFreedExpr = 0 | +| allocators.cpp:81:3:81:45 | delete[] | getFreedExpr = 0 | +| allocators.cpp:82:3:82:49 | delete[] | getFreedExpr = 0 | +| allocators.cpp:83:3:83:23 | delete[] | getFreedExpr = call to GetPointer | +| allocators.cpp:150:2:150:16 | call to operator delete | getFreedExpr = ptr | diff --git a/cpp/ql/test/library-tests/allocators/allocators.ql b/cpp/ql/test/library-tests/allocators/allocators.ql index 37603f77a3ef..e45667fbf018 100644 --- a/cpp/ql/test/library-tests/allocators/allocators.ql +++ b/cpp/ql/test/library-tests/allocators/allocators.ql @@ -1,6 +1,9 @@ import default +import semmle.code.cpp.models.implementations.Allocation -query predicate newExprs(NewExpr expr, string type, string sig, int size, int alignment, string form) { +query predicate newExprs( + NewExpr expr, string type, string sig, int size, int alignment, string form, string placement +) { exists(Function allocator, Type allocatedType | expr.getAllocator() = allocator and sig = allocator.getFullSignature() and @@ -8,13 +11,16 @@ query predicate newExprs(NewExpr expr, string type, string sig, int size, int al type = allocatedType.toString() and size = allocatedType.getSize() and alignment = allocatedType.getAlignment() and - if expr.hasAlignedAllocation() then form = "aligned" else form = "" + (if expr.hasAlignedAllocation() then form = "aligned" else form = "") and + if exists(expr.getPlacementPointer()) + then placement = expr.getPlacementPointer().toString() + else placement = "" ) } query predicate newArrayExprs( NewArrayExpr expr, string t1, string t2, string sig, int size, int alignment, string form, - string extents + string extents, string placement ) { exists(Function allocator, Type arrayType, Type elementType | expr.getAllocator() = allocator and @@ -26,7 +32,10 @@ query predicate newArrayExprs( size = elementType.getSize() and alignment = elementType.getAlignment() and (if expr.hasAlignedAllocation() then form = "aligned" else form = "") and - extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ") + extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ") and + if exists(expr.getPlacementPointer()) + then placement = expr.getPlacementPointer().toString() + else placement = "" ) } @@ -101,3 +110,54 @@ query predicate deleteArrayExprs( ) ) } + +string describeAllocationFunction(AllocationFunction f) { + result = "getSizeArg = " + f.getSizeArg().toString() + or + result = "getSizeMult = " + f.getSizeMult().toString() + or + result = "getReallocPtrArg = " + f.getReallocPtrArg().toString() + or + f.requiresDealloc() and + result = "requiresDealloc" + or + result = + "getPlacementArgument = " + f.(OperatorNewAllocationFunction).getPlacementArgument().toString() +} + +query predicate allocationFunctions(AllocationFunction f, string descr) { + descr = concat(describeAllocationFunction(f), ", ") +} + +string describeAllocationExpr(AllocationExpr e) { + result = "getSizeExpr = " + e.getSizeExpr().toString() + or + result = "getSizeMult = " + e.getSizeMult().toString() + or + result = "getSizeBytes = " + e.getSizeBytes().toString() + or + result = "getReallocPtr = " + e.getReallocPtr().toString() + or + e.requiresDealloc() and + result = "requiresDealloc" +} + +query predicate allocationExprs(AllocationExpr e, string descr) { + descr = concat(describeAllocationExpr(e), ", ") +} + +string describeDeallocationFunction(DeallocationFunction f) { + result = "getFreedArg = " + f.getFreedArg().toString() +} + +query predicate deallocationFunctions(DeallocationFunction f, string descr) { + descr = concat(describeDeallocationFunction(f), ", ") +} + +string describeDeallocationExpr(DeallocationExpr e) { + result = "getFreedExpr = " + e.getFreedExpr().toString() +} + +query predicate deallocationExprs(DeallocationExpr e, string descr) { + descr = concat(describeDeallocationExpr(e), ", ") +} diff --git a/cpp/ql/test/library-tests/allocators/placement.expected b/cpp/ql/test/library-tests/allocators/placement.expected deleted file mode 100644 index 5755e349eb30..000000000000 --- a/cpp/ql/test/library-tests/allocators/placement.expected +++ /dev/null @@ -1,2 +0,0 @@ -| allocators.cpp:129:3:129:21 | new | allocators.cpp:129:7:129:13 | & ... | -| allocators.cpp:132:3:132:17 | new[] | allocators.cpp:132:7:132:9 | buf | diff --git a/cpp/ql/test/library-tests/allocators/placement.ql b/cpp/ql/test/library-tests/allocators/placement.ql deleted file mode 100644 index a632c3c5afe1..000000000000 --- a/cpp/ql/test/library-tests/allocators/placement.ql +++ /dev/null @@ -1,4 +0,0 @@ -import cpp - -from NewOrNewArrayExpr new -select new, new.getPlacementPointer() as placement