Skip to content

Commit dbdb433

Browse files
authored
Merge pull request github#14058 from alexet/delete-or-delete-array
CPP: Add parent class for delete and delete[]
2 parents 5f4861f + ea2140d commit dbdb433

File tree

10 files changed

+80
-132
lines changed

10 files changed

+80
-132
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: deprecated
3+
---
4+
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.

cpp/ql/lib/semmle/code/cpp/PrintAST.qll

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -826,17 +826,11 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
826826
or
827827
expr.(Conversion).getExpr() = ele and pred = "getExpr()"
828828
or
829-
expr.(DeleteArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
829+
expr.(DeleteOrDeleteArrayExpr).getDeallocatorCall() = ele and pred = "getDeallocatorCall()"
830830
or
831-
expr.(DeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
831+
expr.(DeleteOrDeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
832832
or
833-
expr.(DeleteArrayExpr).getExpr() = ele and pred = "getExpr()"
834-
or
835-
expr.(DeleteExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
836-
or
837-
expr.(DeleteExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
838-
or
839-
expr.(DeleteExpr).getExpr() = ele and pred = "getExpr()"
833+
expr.(DeleteOrDeleteArrayExpr).getExpr() = ele and pred = "getExpr()"
840834
or
841835
expr.(DestructorFieldDestruction).getExpr() = ele and pred = "getExpr()"
842836
or

cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -332,21 +332,12 @@ private Node getControlOrderChildSparse(Node n, int i) {
332332
n = any(ConditionDeclExpr cd | i = 0 and result = cd.getInitializingExpr())
333333
or
334334
n =
335-
any(DeleteExpr del |
335+
any(DeleteOrDeleteArrayExpr del |
336336
i = 0 and result = del.getExpr()
337337
or
338338
i = 1 and result = del.getDestructorCall()
339339
or
340-
i = 2 and result = del.getAllocatorCall()
341-
)
342-
or
343-
n =
344-
any(DeleteArrayExpr del |
345-
i = 0 and result = del.getExpr()
346-
or
347-
i = 1 and result = del.getDestructorCall()
348-
or
349-
i = 2 and result = del.getAllocatorCall()
340+
i = 2 and result = del.getDeallocatorCall()
350341
)
351342
or
352343
n =

cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll

Lines changed: 52 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -932,52 +932,50 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
932932
Expr getExtent() { result = this.getChild(2) }
933933
}
934934

935+
private class TDeleteOrDeleteArrayExpr = @delete_expr or @delete_array_expr;
936+
935937
/**
936-
* A C++ `delete` (non-array) expression.
937-
* ```
938-
* delete ptr;
939-
* ```
938+
* A C++ `delete` or `delete[]` expression.
940939
*/
941-
class DeleteExpr extends Expr, @delete_expr {
942-
override string toString() { result = "delete" }
943-
944-
override string getAPrimaryQlClass() { result = "DeleteExpr" }
945-
940+
class DeleteOrDeleteArrayExpr extends Expr, TDeleteOrDeleteArrayExpr {
946941
override int getPrecedence() { result = 16 }
947942

948-
/**
949-
* Gets the compile-time type of the object being deleted.
950-
*/
951-
Type getDeletedObjectType() {
952-
result =
953-
this.getExpr()
954-
.getFullyConverted()
955-
.getType()
956-
.stripTopLevelSpecifiers()
957-
.(PointerType)
958-
.getBaseType()
959-
}
960-
961943
/**
962944
* Gets the call to a destructor that occurs prior to the object's memory being deallocated, if any.
945+
*
946+
* In the case of `delete[]` at runtime, the destructor will be called once for each element in the array, but the
947+
* destructor call only exists once in the AST.
963948
*/
964949
DestructorCall getDestructorCall() { result = this.getChild(1) }
965950

966951
/**
967-
* Gets the destructor to be called to destroy the object, if any.
952+
* Gets the destructor to be called to destroy the object or array, if any.
968953
*/
969954
Destructor getDestructor() { result = this.getDestructorCall().getTarget() }
970955

971956
/**
972-
* Gets the `operator delete` that deallocates storage. Does not hold
973-
* if the type being destroyed has a virtual destructor. In that case, the
957+
* Gets the `operator delete` or `operator delete[]` that deallocates storage.
958+
* Does not hold if the type being destroyed has a virtual destructor. In that case, the
974959
* `operator delete` that will be called is determined at runtime based on the
975960
* dynamic type of the object.
976961
*/
977962
Function getDeallocator() {
978963
expr_deallocator(underlyingElement(this), unresolveElement(result), _)
979964
}
980965

966+
/**
967+
* DEPRECATED: use `getDeallocatorCall` instead.
968+
*/
969+
deprecated FunctionCall getAllocatorCall() { result = this.getChild(0) }
970+
971+
/**
972+
* Gets the call to a non-default `operator delete`/`delete[]` that deallocates storage, if any.
973+
*
974+
* This will only be present when the type being deleted has a custom `operator delete` and
975+
* does not have a virtual destructor.
976+
*/
977+
FunctionCall getDeallocatorCall() { result = this.getChild(0) }
978+
981979
/**
982980
* Holds if the deallocation function expects a size argument.
983981
*/
@@ -999,16 +997,38 @@ class DeleteExpr extends Expr, @delete_expr {
999997
}
1000998

1001999
/**
1002-
* Gets the call to a non-default `operator delete` that deallocates storage, if any.
1003-
*
1004-
* This will only be present when the type being deleted has a custom `operator delete`.
1000+
* Gets the object or array being deleted.
10051001
*/
1006-
FunctionCall getAllocatorCall() { result = this.getChild(0) }
1002+
Expr getExpr() {
1003+
// If there is a destructor call, the object being deleted is the qualifier
1004+
// otherwise it is the third child.
1005+
result = this.getChild(3) or result = this.getDestructorCall().getQualifier()
1006+
}
1007+
}
1008+
1009+
/**
1010+
* A C++ `delete` (non-array) expression.
1011+
* ```
1012+
* delete ptr;
1013+
* ```
1014+
*/
1015+
class DeleteExpr extends DeleteOrDeleteArrayExpr, @delete_expr {
1016+
override string toString() { result = "delete" }
1017+
1018+
override string getAPrimaryQlClass() { result = "DeleteExpr" }
10071019

10081020
/**
1009-
* Gets the object being deleted.
1021+
* Gets the compile-time type of the object being deleted.
10101022
*/
1011-
Expr getExpr() { result = this.getChild(3) or result = this.getChild(1).getChild(-1) }
1023+
Type getDeletedObjectType() {
1024+
result =
1025+
this.getExpr()
1026+
.getFullyConverted()
1027+
.getType()
1028+
.stripTopLevelSpecifiers()
1029+
.(PointerType)
1030+
.getBaseType()
1031+
}
10121032
}
10131033

10141034
/**
@@ -1017,13 +1037,11 @@ class DeleteExpr extends Expr, @delete_expr {
10171037
* delete[] arr;
10181038
* ```
10191039
*/
1020-
class DeleteArrayExpr extends Expr, @delete_array_expr {
1040+
class DeleteArrayExpr extends DeleteOrDeleteArrayExpr, @delete_array_expr {
10211041
override string toString() { result = "delete[]" }
10221042

10231043
override string getAPrimaryQlClass() { result = "DeleteArrayExpr" }
10241044

1025-
override int getPrecedence() { result = 16 }
1026-
10271045
/**
10281046
* Gets the element type of the array being deleted.
10291047
*/
@@ -1036,58 +1054,6 @@ class DeleteArrayExpr extends Expr, @delete_array_expr {
10361054
.(PointerType)
10371055
.getBaseType()
10381056
}
1039-
1040-
/**
1041-
* Gets the call to a destructor that occurs prior to the array's memory being deallocated, if any.
1042-
*
1043-
* At runtime, the destructor will be called once for each element in the array, but the
1044-
* destructor call only exists once in the AST.
1045-
*/
1046-
DestructorCall getDestructorCall() { result = this.getChild(1) }
1047-
1048-
/**
1049-
* Gets the destructor to be called to destroy each element in the array, if any.
1050-
*/
1051-
Destructor getDestructor() { result = this.getDestructorCall().getTarget() }
1052-
1053-
/**
1054-
* Gets the `operator delete[]` that deallocates storage.
1055-
*/
1056-
Function getDeallocator() {
1057-
expr_deallocator(underlyingElement(this), unresolveElement(result), _)
1058-
}
1059-
1060-
/**
1061-
* Holds if the deallocation function expects a size argument.
1062-
*/
1063-
predicate hasSizedDeallocation() {
1064-
exists(int form |
1065-
expr_deallocator(underlyingElement(this), _, form) and
1066-
form.bitAnd(1) != 0 // Bit zero is the "size" bit
1067-
)
1068-
}
1069-
1070-
/**
1071-
* Holds if the deallocation function expects an alignment argument.
1072-
*/
1073-
predicate hasAlignedDeallocation() {
1074-
exists(int form |
1075-
expr_deallocator(underlyingElement(this), _, form) and
1076-
form.bitAnd(2) != 0 // Bit one is the "alignment" bit
1077-
)
1078-
}
1079-
1080-
/**
1081-
* Gets the call to a non-default `operator delete` that deallocates storage, if any.
1082-
*
1083-
* This will only be present when the type being deleted has a custom `operator delete`.
1084-
*/
1085-
FunctionCall getAllocatorCall() { result = this.getChild(0) }
1086-
1087-
/**
1088-
* Gets the array being deleted.
1089-
*/
1090-
Expr getExpr() { result = this.getChild(3) or result = this.getChild(1).getChild(-1) }
10911057
}
10921058

10931059
/**

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ private predicate ignoreExprAndDescendants(Expr expr) {
8484
or
8585
// We do not yet translate destructors properly, so for now we ignore any
8686
// custom deallocator call, if present.
87-
exists(DeleteExpr deleteExpr | deleteExpr.getAllocatorCall() = expr)
87+
exists(DeleteExpr deleteExpr | deleteExpr.getDeallocatorCall() = expr)
8888
or
89-
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getAllocatorCall() = expr)
89+
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getDeallocatorCall() = expr)
9090
or
9191
exists(BuiltInVarArgsStart vaStartExpr |
9292
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr

cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,6 @@ import cpp
1717
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
1818
import semmle.code.cpp.controlflow.Guards
1919

20-
/**
21-
* A C++ `delete` or `delete[]` expression.
22-
*/
23-
class DeleteOrDeleteArrayExpr extends Expr {
24-
DeleteOrDeleteArrayExpr() { this instanceof DeleteExpr or this instanceof DeleteArrayExpr }
25-
26-
DeallocationFunction getDeallocator() {
27-
result = [this.(DeleteExpr).getDeallocator(), this.(DeleteArrayExpr).getDeallocator()]
28-
}
29-
30-
Destructor getDestructor() {
31-
result = [this.(DeleteExpr).getDestructor(), this.(DeleteArrayExpr).getDestructor()]
32-
}
33-
}
34-
3520
/** Gets the `Constructor` invoked when `newExpr` allocates memory. */
3621
Constructor getConstructorForAllocation(NewOrNewArrayExpr newExpr) {
3722
result.getACallToThisFunction() = newExpr.getInitializer()

cpp/ql/test/library-tests/allocators/allocators.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ newArrayExprDeallocators
3434
| allocators.cpp:108:3:108:19 | new[] | FailedInit | void FailedInit::operator delete[](void*, size_t) | 1 | 1 | sized |
3535
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned | void FailedInitOveraligned::operator delete[](void*, std::align_val_t, float) | 128 | 128 | aligned |
3636
deleteExprs
37-
| allocators.cpp:59:3:59:35 | delete | int | void operator delete(void*, unsigned long) | 4 | 4 | sized |
38-
| allocators.cpp:60:3:60:38 | delete | String | void operator delete(void*, unsigned long) | 8 | 8 | sized |
39-
| allocators.cpp:61:3:61:44 | delete | SizedDealloc | void SizedDealloc::operator delete(void*, size_t) | 32 | 1 | sized |
40-
| allocators.cpp:62:3:62:43 | delete | Overaligned | void operator delete(void*, unsigned long, std::align_val_t) | 256 | 128 | sized aligned |
41-
| allocators.cpp:64:3:64:44 | delete | const String | void operator delete(void*, unsigned long) | 8 | 8 | sized |
37+
| allocators.cpp:59:3:59:35 | delete | int | void operator delete(void*, unsigned long) | 4 | 4 | sized | false |
38+
| allocators.cpp:60:3:60:38 | delete | String | void operator delete(void*, unsigned long) | 8 | 8 | sized | false |
39+
| allocators.cpp:61:3:61:44 | delete | SizedDealloc | void SizedDealloc::operator delete(void*, size_t) | 32 | 1 | sized | true |
40+
| allocators.cpp:62:3:62:43 | delete | Overaligned | void operator delete(void*, unsigned long, std::align_val_t) | 256 | 128 | sized aligned | false |
41+
| allocators.cpp:64:3:64:44 | delete | const String | void operator delete(void*, unsigned long) | 8 | 8 | sized | false |
4242
deleteArrayExprs
4343
| allocators.cpp:78:3:78:37 | delete[] | int | void operator delete[](void*, unsigned long) | 4 | 4 | sized |
4444
| allocators.cpp:79:3:79:40 | delete[] | String | void operator delete[](void*, unsigned long) | 8 | 8 | sized |

cpp/ql/test/library-tests/allocators/allocators.ql

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ query predicate newArrayExprDeallocators(
7777
}
7878

7979
query predicate deleteExprs(
80-
DeleteExpr expr, string type, string sig, int size, int alignment, string form
80+
DeleteExpr expr, string type, string sig, int size, int alignment, string form,
81+
boolean hasDeallocatorCall
8182
) {
8283
exists(Function deallocator, Type deletedType |
8384
expr.getDeallocator() = deallocator and
@@ -90,7 +91,10 @@ query predicate deleteExprs(
9091
(if expr.hasAlignedDeallocation() then aligned = "aligned" else aligned = "") and
9192
(if expr.hasSizedDeallocation() then sized = "sized" else sized = "") and
9293
form = sized + " " + aligned
93-
)
94+
) and
95+
if exists(expr.getDeallocatorCall())
96+
then hasDeallocatorCall = true
97+
else hasDeallocatorCall = false
9498
)
9599
}
96100

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8477,7 +8477,7 @@ ir.cpp:
84778477
# 1018| getExpr(): [DeleteExpr] delete
84788478
# 1018| Type = [VoidType] void
84798479
# 1018| ValueCategory = prvalue
8480-
# 1018| getAllocatorCall(): [FunctionCall] call to operator delete
8480+
# 1018| getDeallocatorCall(): [FunctionCall] call to operator delete
84818481
# 1018| Type = [VoidType] void
84828482
# 1018| ValueCategory = prvalue
84838483
# 1018| getExpr(): [Literal] 0
@@ -8555,7 +8555,7 @@ ir.cpp:
85558555
# 1027| getExpr(): [DeleteArrayExpr] delete[]
85568556
# 1027| Type = [VoidType] void
85578557
# 1027| ValueCategory = prvalue
8558-
# 1027| getAllocatorCall(): [FunctionCall] call to operator delete[]
8558+
# 1027| getDeallocatorCall(): [FunctionCall] call to operator delete[]
85598559
# 1027| Type = [VoidType] void
85608560
# 1027| ValueCategory = prvalue
85618561
# 1027| getExpr(): [Literal] 0

0 commit comments

Comments
 (0)