Skip to content

C++: Support operator new and operator delete in models library #3173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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())
}
}

Expand Down Expand Up @@ -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() }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
Expand Down
38 changes: 37 additions & 1 deletion cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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()) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the overloads where this predicate holds (9 and 10 in https://en.cppreference.com/w/cpp/memory/new/operator_new), I don't think they should be AllocationFunctions. They are merely identity functions. The IR alias analysis assumes that an allocation function returns a fresh memory location that doesn't alias anything else reachable, and that's also my own understanding of what an allocation function is.

The requiresDealloc predicate is currently overridden by alloca, and that makes sense to me: it returns fresh memory but doesn't require deallocation. In the AllocationExpr hierarchy, requiresDealloc is overridden by NewAllocationExpr and NewArrayAllocationExpr, and I think the placement versions of those expressions should not be AllocationExprs for the same reasons. Unless the allocation classes are used in other queries where we do want to include the placement versions.

Functions that always return their parameter can instead be modelled as AliasFunction.

There may be some C++ standard rules about how it's forbidden to reference an old pointer value that's passed through placement new, which effectively means it behaves like it's allocating fresh memory, but I don't think programmers obey that rule. I also couldn't find the rule on https://en.cppreference.com/w/cpp/language/new, so I may be misremembering.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IR alias analysis assumes that an allocation function returns a fresh memory location that doesn't alias anything else reachable

That's a slightly dubious assumption when you're looking inside the implementation of an operator new, which presumably needs to keep hold of some data about the allocation for bookkeeping purposes (I'm not sure exactly how modern non-toy implementations do this). But that data should be well compartmentalized from user code, so I doubt this is something we need to model.

Functions that always return their parameter can instead be modelled as AliasFunction.

Good idea. On balance, I think your distinction is clearer than mine (which is roughly 'Does this thing claim to be an allocator? Then it is one.').

However due to the existing behaviour in AllocationExpr (which is fairly widely used), I'd rather address this concern in a follow-up PR and give it the care and attention it deserves.

There may be some C++ standard rules about how it's forbidden to reference an old pointer value that's passed through placement new

It seems like you should be able to keep a reference for memory management purposes, e.g.

char *data = new char[BIG_NUMBER];

MyObject *myObject1 = new (data) MyObject();
MyObject *myObject2 = new (data + sizeof(MyObject)) MyObject();
...

delete [] data;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However due to the existing behaviour in AllocationExpr (which is fairly widely used), I'd rather address this concern in a follow-up PR and give it the care and attention it deserves.

Okay.

It seems like you should be able to keep a reference for memory management purposes

That sounds right.


/**
* 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`.
*/
Expand All @@ -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()) }
Expand Down
22 changes: 22 additions & 0 deletions cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
*/
Expand Down
6 changes: 6 additions & 0 deletions cpp/ql/test/library-tests/allocators/allocators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
108 changes: 85 additions & 23 deletions cpp/ql/test/library-tests/allocators/allocators.expected
Original file line number Diff line number Diff line change
@@ -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 | |
Expand All @@ -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 |
68 changes: 64 additions & 4 deletions cpp/ql/test/library-tests/allocators/allocators.ql
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
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
allocatedType = expr.getAllocatedType() and
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
Expand All @@ -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 = ""
)
}

Expand Down Expand Up @@ -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), ", ")
}
2 changes: 0 additions & 2 deletions cpp/ql/test/library-tests/allocators/placement.expected

This file was deleted.

4 changes: 0 additions & 4 deletions cpp/ql/test/library-tests/allocators/placement.ql

This file was deleted.