Skip to content

Commit 3b344c3

Browse files
committed
CPP: Handle cases where the deallocator function is determined dynamically.
1 parent 689fda4 commit 3b344c3

File tree

10 files changed

+163
-3
lines changed

10 files changed

+163
-3
lines changed

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+
/**
581+
* An instruction that returns the address of a "virtual" delete function.
582+
*
583+
* This function, which does not actually exist in the source code, is used to
584+
* delete objects of a class with a virtual destructor. In that case the deacllocation
585+
* function is selected at runtime based on the dynamic type of the object. So this
586+
* function dynamically dispatches to the correct deallocation function.
587+
* It also should pass in the required extra arguments to the deallocation function
588+
* which may differ dynamically depending on the type of the object.
589+
*/
590+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
591+
592+
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
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+
/**
581+
* An instruction that returns the address of a "virtual" delete function.
582+
*
583+
* This function, which does not actually exist in the source code, is used to
584+
* delete objects of a class with a virtual destructor. In that case the deacllocation
585+
* function is selected at runtime based on the dynamic type of the object. So this
586+
* function dynamically dispatches to the correct deallocation function.
587+
* It also should pass in the required extra arguments to the deallocation function
588+
* which may differ dynamically depending on the type of the object.
589+
*/
590+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
591+
592+
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ 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 {

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2021,13 +2021,37 @@ TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
20212021
* The IR translation of a call to `operator delete` as part of a `delete` or `delete[]`
20222022
* expression.
20232023
*/
2024-
class TranslatedDeallocatorCall extends TTranslatedDeallocatorCall, TranslatedDirectCall {
2024+
class TranslatedDeallocatorCall extends TTranslatedDeallocatorCall, TranslatedCall {
20252025
override DeleteOrDeleteArrayExpr expr;
20262026

20272027
TranslatedDeallocatorCall() { this = TTranslatedDeallocatorCall(expr) }
20282028

20292029
final override string toString() { result = "Deallocator call for " + expr.toString() }
20302030

2031+
final override Instruction getFirstCallTargetInstruction() {
2032+
result = this.getInstruction(CallTargetTag())
2033+
}
2034+
2035+
final override Instruction getCallTargetResult() { result = this.getInstruction(CallTargetTag()) }
2036+
2037+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
2038+
TranslatedCall.super.hasInstruction(opcode, tag, resultType)
2039+
or
2040+
tag = CallTargetTag() and
2041+
resultType = getFunctionGLValueType() and
2042+
if exists(expr.getDeallocator())
2043+
then opcode instanceof Opcode::FunctionAddress
2044+
else opcode instanceof Opcode::VirtualDeleteFunctionAddress
2045+
}
2046+
2047+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
2048+
result = TranslatedCall.super.getInstructionSuccessor(tag, kind)
2049+
or
2050+
tag = CallTargetTag() and
2051+
kind instanceof GotoEdge and
2052+
result = this.getFirstArgumentOrCallInstruction()
2053+
}
2054+
20312055
final override predicate producesExprResult() { none() }
20322056

20332057
override Function getInstructionFunction(InstructionTag tag) {

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+
/**
581+
* An instruction that returns the address of a "virtual" delete function.
582+
*
583+
* This function, which does not actually exist in the source code, is used to
584+
* delete objects of a class with a virtual destructor. In that case the deacllocation
585+
* function is selected at runtime based on the dynamic type of the object. So this
586+
* function dynamically dispatches to the correct deallocation function.
587+
* It also should pass in the required extra arguments to the deallocation function
588+
* which may differ dynamically depending on the type of the object.
589+
*/
590+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
591+
592+
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
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/test/library-tests/ir/ir/ir.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,4 +2030,40 @@ unsigned int CommaTest(unsigned int x) {
20302030
(CommaTestHelper(x), 10);
20312031
}
20322032

2033+
void NewDeleteMem() {
2034+
int* x = new int; // No constructor
2035+
*x = 6;
2036+
delete x;
2037+
}
2038+
2039+
class Base2 {
2040+
public:
2041+
void operator delete(void* p) {
2042+
}
2043+
virtual ~Base2() {};
2044+
};
2045+
2046+
class Derived2 : public Base2 {
2047+
int i;
2048+
public:
2049+
~Derived2() {};
2050+
2051+
void operator delete(void* p) {
2052+
}
2053+
};
2054+
2055+
// Delete is kind-of virtual in these cases
2056+
int virtual_delete()
2057+
{
2058+
Base2* b1 = new Base2{};
2059+
delete b1;
2060+
2061+
Base2* b2 = new Derived2{};
2062+
delete b2;
2063+
2064+
Derived2* d = new Derived2{};
2065+
delete d;
2066+
}
2067+
2068+
20332069
// semmle-extractor-options: -std=c++17 --clang

csharp/ql/src/experimental/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
*

csharp/ql/src/experimental/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+
/**
581+
* An instruction that returns the address of a "virtual" delete function.
582+
*
583+
* This function, which does not actually exist in the source code, is used to
584+
* delete objects of a class with a virtual destructor. In that case the deacllocation
585+
* function is selected at runtime based on the dynamic type of the object. So this
586+
* function dynamically dispatches to the correct deallocation function.
587+
* It also should pass in the required extra arguments to the deallocation function
588+
* which may differ dynamically depending on the type of the object.
589+
*/
590+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
591+
592+
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
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.

csharp/ql/src/experimental/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+
/**
581+
* An instruction that returns the address of a "virtual" delete function.
582+
*
583+
* This function, which does not actually exist in the source code, is used to
584+
* delete objects of a class with a virtual destructor. In that case the deacllocation
585+
* function is selected at runtime based on the dynamic type of the object. So this
586+
* function dynamically dispatches to the correct deallocation function.
587+
* It also should pass in the required extra arguments to the deallocation function
588+
* which may differ dynamically depending on the type of the object.
589+
*/
590+
class VirtualDeleteFunctionAddressInstruction extends Instruction {
591+
592+
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
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)