CPP: Handle cases where the deallocator function is determined dynamically.

This commit is contained in:
Alex Eyers-Taylor
2023-08-23 19:36:09 +01:00
parent 689fda43ed
commit 3b344c3578
10 changed files with 163 additions and 3 deletions

View File

@@ -55,6 +55,7 @@ private newtype TOpcode =
TVariableAddress() or
TFieldAddress() or
TFunctionAddress() or
TVirtualDeleteFunctionAddress() or
TElementsAddress() or
TConstant() or
TStringConstant() or
@@ -887,6 +888,15 @@ module Opcode {
final override string toString() { result = "FunctionAddress" }
}
/**
* The `Opcode` for a `VirtualDeleteFunctionAddress`.
*
* See the `VirtualDeleteFunctionAddressInstruction` documentation for more details.
*/
class VirtualDeleteFunctionAddress extends Opcode, TVirtualDeleteFunctionAddress {
final override string toString() { result = "VirtualDeleteFunctionAddress" }
}
/**
* The `Opcode` for a `ConstantInstruction`.
*

View File

@@ -576,6 +576,22 @@ class FunctionAddressInstruction extends FunctionInstruction {
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
}
/**
* An instruction that returns the address of a "virtual" delete function.
*
* This function, which does not actually exist in the source code, is used to
* delete objects of a class with a virtual destructor. In that case the deacllocation
* function is selected at runtime based on the dynamic type of the object. So this
* function dynamically dispatches to the correct deallocation function.
* It also should pass in the required extra arguments to the deallocation function
* which may differ dynamically depending on the type of the object.
*/
class VirtualDeleteFunctionAddressInstruction extends Instruction {
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
}
/**
* An instruction that initializes a parameter of the enclosing function with the value of the
* corresponding argument passed by the caller.

View File

@@ -576,6 +576,22 @@ class FunctionAddressInstruction extends FunctionInstruction {
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
}
/**
* An instruction that returns the address of a "virtual" delete function.
*
* This function, which does not actually exist in the source code, is used to
* delete objects of a class with a virtual destructor. In that case the deacllocation
* function is selected at runtime based on the dynamic type of the object. So this
* function dynamically dispatches to the correct deallocation function.
* It also should pass in the required extra arguments to the deallocation function
* which may differ dynamically depending on the type of the object.
*/
class VirtualDeleteFunctionAddressInstruction extends Instruction {
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
}
/**
* An instruction that initializes a parameter of the enclosing function with the value of the
* corresponding argument passed by the caller.

View File

@@ -120,9 +120,9 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
}
/**
* A `Call` or `NewOrNewArrayExpr`.
* A `Call` or `NewOrNewArrayExpr` or `DeleteOrDeleteArrayExpr`.
*
* Both kinds of expression invoke a function as part of their evaluation. This class provides a
* All kinds of expression invoke a function as part of their evaluation. This class provides a
* way to treat both kinds of function similarly, and to get the invoked `Function`.
*/
class CallOrAllocationExpr extends Expr {

View File

@@ -2021,13 +2021,37 @@ TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
* The IR translation of a call to `operator delete` as part of a `delete` or `delete[]`
* expression.
*/
class TranslatedDeallocatorCall extends TTranslatedDeallocatorCall, TranslatedDirectCall {
class TranslatedDeallocatorCall extends TTranslatedDeallocatorCall, TranslatedCall {
override DeleteOrDeleteArrayExpr expr;
TranslatedDeallocatorCall() { this = TTranslatedDeallocatorCall(expr) }
final override string toString() { result = "Deallocator call for " + expr.toString() }
final override Instruction getFirstCallTargetInstruction() {
result = this.getInstruction(CallTargetTag())
}
final override Instruction getCallTargetResult() { result = this.getInstruction(CallTargetTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedCall.super.hasInstruction(opcode, tag, resultType)
or
tag = CallTargetTag() and
resultType = getFunctionGLValueType() and
if exists(expr.getDeallocator())
then opcode instanceof Opcode::FunctionAddress
else opcode instanceof Opcode::VirtualDeleteFunctionAddress
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
result = TranslatedCall.super.getInstructionSuccessor(tag, kind)
or
tag = CallTargetTag() and
kind instanceof GotoEdge and
result = this.getFirstArgumentOrCallInstruction()
}
final override predicate producesExprResult() { none() }
override Function getInstructionFunction(InstructionTag tag) {

View File

@@ -576,6 +576,22 @@ class FunctionAddressInstruction extends FunctionInstruction {
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
}
/**
* An instruction that returns the address of a "virtual" delete function.
*
* This function, which does not actually exist in the source code, is used to
* delete objects of a class with a virtual destructor. In that case the deacllocation
* function is selected at runtime based on the dynamic type of the object. So this
* function dynamically dispatches to the correct deallocation function.
* It also should pass in the required extra arguments to the deallocation function
* which may differ dynamically depending on the type of the object.
*/
class VirtualDeleteFunctionAddressInstruction extends Instruction {
VirtualDeleteFunctionAddressInstruction() { this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress }
}
/**
* An instruction that initializes a parameter of the enclosing function with the value of the
* corresponding argument passed by the caller.

View File

@@ -2030,4 +2030,40 @@ unsigned int CommaTest(unsigned int x) {
(CommaTestHelper(x), 10);
}
void NewDeleteMem() {
int* x = new int; // No constructor
*x = 6;
delete x;
}
class Base2 {
public:
void operator delete(void* p) {
}
virtual ~Base2() {};
};
class Derived2 : public Base2 {
int i;
public:
~Derived2() {};
void operator delete(void* p) {
}
};
// Delete is kind-of virtual in these cases
int virtual_delete()
{
Base2* b1 = new Base2{};
delete b1;
Base2* b2 = new Derived2{};
delete b2;
Derived2* d = new Derived2{};
delete d;
}
// semmle-extractor-options: -std=c++17 --clang