Skip to content

Commit f5509da

Browse files
authored
Merge pull request github#14038 from alexet/delete-ir
CPP: Add delete/delete[] calls to the IR.
2 parents 2d5c40d + 59a7766 commit f5509da

File tree

22 files changed

+915
-125
lines changed

22 files changed

+915
-125
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.

cpp/ql/lib/semmle/code/cpp/ir/implementation/Opcode.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ private newtype TOpcode =
5555
TVariableAddress() or
5656
TFieldAddress() or
5757
TFunctionAddress() or
58+
TVirtualDeleteFunctionAddress() or
5859
TElementsAddress() or
5960
TConstant() or
6061
TStringConstant() or
@@ -887,6 +888,15 @@ module Opcode {
887888
final override string toString() { result = "FunctionAddress" }
888889
}
889890

891+
/**
892+
* The `Opcode` for a `VirtualDeleteFunctionAddress`.
893+
*
894+
* See the `VirtualDeleteFunctionAddressInstruction` documentation for more details.
895+
*/
896+
class VirtualDeleteFunctionAddress extends Opcode, TVirtualDeleteFunctionAddress {
897+
final override string toString() { result = "VirtualDeleteFunctionAddress" }
898+
}
899+
890900
/**
891901
* The `Opcode` for a `ConstantInstruction`.
892902
*

cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,22 @@ class FunctionAddressInstruction extends FunctionInstruction {
576576
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
577577
}
578578

579+
/**
580+
* An instruction that returns the address of a "virtual" delete function.
581+
*
582+
* This function, which does not actually exist in the source code, is used to
583+
* delete objects of a class with a virtual destructor. In that case the deacllocation
584+
* function is selected at runtime based on the dynamic type of the object. So this
585+
* function dynamically dispatches to the correct deallocation function.
586+
* It also should pass in the required extra arguments to the deallocation function
587+
* which may differ dynamically depending on the type of the object.
588+
*/
589+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
590+
VirtualDeleteFunctionAddressInstruction() {
591+
this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress
592+
}
593+
}
594+
579595
/**
580596
* An instruction that initializes a parameter of the enclosing function with the value of the
581597
* corresponding argument passed by the caller.

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,22 @@ class FunctionAddressInstruction extends FunctionInstruction {
576576
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
577577
}
578578

579+
/**
580+
* An instruction that returns the address of a "virtual" delete function.
581+
*
582+
* This function, which does not actually exist in the source code, is used to
583+
* delete objects of a class with a virtual destructor. In that case the deacllocation
584+
* function is selected at runtime based on the dynamic type of the object. So this
585+
* function dynamically dispatches to the correct deallocation function.
586+
* It also should pass in the required extra arguments to the deallocation function
587+
* which may differ dynamically depending on the type of the object.
588+
*/
589+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
590+
VirtualDeleteFunctionAddressInstruction() {
591+
this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress
592+
}
593+
}
594+
579595
/**
580596
* An instruction that initializes a parameter of the enclosing function with the value of the
581597
* corresponding argument passed by the caller.

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,27 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
120120
}
121121

122122
/**
123-
* A `Call` or `NewOrNewArrayExpr`.
123+
* A `Call` or `NewOrNewArrayExpr` or `DeleteOrDeleteArrayExpr`.
124124
*
125-
* Both kinds of expression invoke a function as part of their evaluation. This class provides a
125+
* All kinds of expression invoke a function as part of their evaluation. This class provides a
126126
* way to treat both kinds of function similarly, and to get the invoked `Function`.
127127
*/
128128
class CallOrAllocationExpr extends Expr {
129129
CallOrAllocationExpr() {
130130
this instanceof Call
131131
or
132132
this instanceof NewOrNewArrayExpr
133+
or
134+
this instanceof DeleteOrDeleteArrayExpr
133135
}
134136

135137
/** Gets the `Function` invoked by this expression, if known. */
136138
final Function getTarget() {
137139
result = this.(Call).getTarget()
138140
or
139141
result = this.(NewOrNewArrayExpr).getAllocator()
142+
or
143+
result = this.(DeleteOrDeleteArrayExpr).getDeallocator()
140144
}
141145
}
142146

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
350350
or
351351
expr instanceof NewOrNewArrayExpr and
352352
result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
353+
or
354+
expr instanceof DeleteOrDeleteArrayExpr and
355+
result = getTranslatedDeleteOrDeleteArray(expr).getInstruction(CallTag())
353356
}
354357
}
355358

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

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,17 @@ private predicate ignoreExprAndDescendants(Expr expr) {
7777
newExpr.getInitializer().getFullyConverted() = expr
7878
)
7979
or
80+
exists(DeleteOrDeleteArrayExpr deleteExpr |
81+
// Ignore the deallocator call, because we always synthesize it.
82+
deleteExpr.getDeallocatorCall() = expr
83+
)
84+
or
8085
// Do not translate input/output variables in GNU asm statements
8186
// getRealParent(expr) instanceof AsmStmt
8287
// or
8388
ignoreExprAndDescendants(getRealParent(expr)) // recursive case
8489
or
85-
// We do not yet translate destructors properly, so for now we ignore any
86-
// custom deallocator call, if present.
87-
exists(DeleteExpr deleteExpr | deleteExpr.getDeallocatorCall() = expr)
88-
or
89-
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getDeallocatorCall() = expr)
90-
or
90+
// va_start doesn't evaluate its argument, so we don't need to translate it.
9191
exists(BuiltInVarArgsStart vaStartExpr |
9292
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
9393
)
@@ -104,20 +104,19 @@ private predicate ignoreExprOnly(Expr expr) {
104104
newExpr.getAllocatorCall() = expr
105105
)
106106
or
107+
exists(DeleteOrDeleteArrayExpr deleteExpr |
108+
// Ignore the destructor call as we don't model it yet. Don't ignore
109+
// its arguments, though, as they are the arguments to the deallocator.
110+
deleteExpr.getDestructorCall() = expr
111+
)
112+
or
107113
// The extractor deliberately emits an `ErrorExpr` as the first argument to
108114
// the allocator call, if any, of a `NewOrNewArrayExpr`. That `ErrorExpr`
109115
// should not be translated.
110116
exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0))
111117
or
112118
not translateFunction(getEnclosingFunction(expr)) and
113119
not Raw::varHasIRFunc(getEnclosingVariable(expr))
114-
or
115-
// We do not yet translate destructors properly, so for now we ignore the
116-
// destructor call. We do, however, translate the expression being
117-
// destructed, and that expression can be a child of the destructor call.
118-
exists(DeleteExpr deleteExpr | deleteExpr.getDestructorCall() = expr)
119-
or
120-
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getDestructorCall() = expr)
121120
}
122121

123122
/**

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

Lines changed: 60 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,66 @@ TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
20172017
result.getAst() = newExpr
20182018
}
20192019

2020+
/**
2021+
* The IR translation of a `delete` or `delete[]`
2022+
* expression.
2023+
*/
2024+
class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, TranslatedCall {
2025+
override DeleteOrDeleteArrayExpr expr;
2026+
2027+
final override Instruction getFirstCallTargetInstruction() {
2028+
result = this.getInstruction(CallTargetTag())
2029+
}
2030+
2031+
final override Instruction getCallTargetResult() { result = this.getInstruction(CallTargetTag()) }
2032+
2033+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
2034+
TranslatedCall.super.hasInstruction(opcode, tag, resultType)
2035+
or
2036+
tag = CallTargetTag() and
2037+
resultType = getFunctionGLValueType() and
2038+
if exists(expr.getDeallocator())
2039+
then opcode instanceof Opcode::FunctionAddress
2040+
else opcode instanceof Opcode::VirtualDeleteFunctionAddress
2041+
}
2042+
2043+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
2044+
result = TranslatedCall.super.getInstructionSuccessor(tag, kind)
2045+
or
2046+
tag = CallTargetTag() and
2047+
kind instanceof GotoEdge and
2048+
result = this.getFirstArgumentOrCallInstruction()
2049+
}
2050+
2051+
override Function getInstructionFunction(InstructionTag tag) {
2052+
tag = CallTargetTag() and result = expr.getDeallocator()
2053+
}
2054+
2055+
final override Type getCallResultType() { result = expr.getType() }
2056+
2057+
final override TranslatedExpr getQualifier() { none() }
2058+
2059+
final override predicate hasArguments() {
2060+
// All deallocator calls have at least one argument.
2061+
any()
2062+
}
2063+
2064+
final override int getNumberOfArguments() {
2065+
// We ignore the other arguments for now as we would have to synthesize them.
2066+
result = 1
2067+
}
2068+
2069+
final override TranslatedExpr getArgument(int index) {
2070+
// The only argument we define is the pointer to be deallocated.
2071+
index = 0 and
2072+
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
2073+
}
2074+
}
2075+
2076+
TranslatedDeleteOrDeleteArrayExpr getTranslatedDeleteOrDeleteArray(DeleteOrDeleteArrayExpr newExpr) {
2077+
result.getAst() = newExpr
2078+
}
2079+
20202080
/**
20212081
* Abstract class implemented by any `TranslatedElement` that has a child
20222082
* expression that is a call to a constructor or destructor, in order to
@@ -2954,78 +3014,6 @@ class TranslatedNewArrayExpr extends TranslatedNewOrNewArrayExpr {
29543014
}
29553015
}
29563016

2957-
/**
2958-
* A placeholder for the translation of a `delete[]` expression.
2959-
*
2960-
* Proper translation is not yet implemented, but this stub implementation
2961-
* ensures that code following a `delete[]` is not unreachable.
2962-
*/
2963-
class TranslatedDeleteArrayExprPlaceHolder extends TranslatedSingleInstructionExpr {
2964-
override DeleteArrayExpr expr;
2965-
2966-
final override Instruction getFirstInstruction() {
2967-
result = this.getOperand().getFirstInstruction()
2968-
}
2969-
2970-
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
2971-
2972-
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
2973-
tag = OnlyInstructionTag() and
2974-
result = this.getParent().getChildSuccessor(this) and
2975-
kind instanceof GotoEdge
2976-
}
2977-
2978-
final override Instruction getChildSuccessor(TranslatedElement child) {
2979-
child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag())
2980-
}
2981-
2982-
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
2983-
none()
2984-
}
2985-
2986-
final override Opcode getOpcode() { result instanceof Opcode::NoOp }
2987-
2988-
private TranslatedExpr getOperand() {
2989-
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
2990-
}
2991-
}
2992-
2993-
/**
2994-
* A placeholder for the translation of a `delete` expression.
2995-
*
2996-
* Proper translation is not yet implemented, but this stub implementation
2997-
* ensures that code following a `delete` is not unreachable.
2998-
*/
2999-
class TranslatedDeleteExprPlaceHolder extends TranslatedSingleInstructionExpr {
3000-
override DeleteExpr expr;
3001-
3002-
final override Instruction getFirstInstruction() {
3003-
result = this.getOperand().getFirstInstruction()
3004-
}
3005-
3006-
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
3007-
3008-
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
3009-
tag = OnlyInstructionTag() and
3010-
result = this.getParent().getChildSuccessor(this) and
3011-
kind instanceof GotoEdge
3012-
}
3013-
3014-
final override Instruction getChildSuccessor(TranslatedElement child) {
3015-
child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag())
3016-
}
3017-
3018-
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
3019-
none()
3020-
}
3021-
3022-
final override Opcode getOpcode() { result instanceof Opcode::NoOp }
3023-
3024-
private TranslatedExpr getOperand() {
3025-
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
3026-
}
3027-
}
3028-
30293017
/**
30303018
* The IR translation of a `ConditionDeclExpr`, which represents the value of the declared variable
30313019
* after conversion to `bool` in code such as:

cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,22 @@ class FunctionAddressInstruction extends FunctionInstruction {
576576
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
577577
}
578578

579+
/**
580+
* An instruction that returns the address of a "virtual" delete function.
581+
*
582+
* This function, which does not actually exist in the source code, is used to
583+
* delete objects of a class with a virtual destructor. In that case the deacllocation
584+
* function is selected at runtime based on the dynamic type of the object. So this
585+
* function dynamically dispatches to the correct deallocation function.
586+
* It also should pass in the required extra arguments to the deallocation function
587+
* which may differ dynamically depending on the type of the object.
588+
*/
589+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
590+
VirtualDeleteFunctionAddressInstruction() {
591+
this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress
592+
}
593+
}
594+
579595
/**
580596
* An instruction that initializes a parameter of the enclosing function with the value of the
581597
* corresponding argument passed by the caller.

0 commit comments

Comments
 (0)