diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index fb25314f230..8335469612a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction { result = getAnOperand() } + /** + * Gets the `Function` that the call targets, if this is statically known. + */ + final Function getStaticCallTarget() { + result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + } + /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 7ad949a1a8c..0ccbcd381bb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -3,6 +3,8 @@ import cpp private import InputIR private import semmle.code.cpp.ir.internal.IntegerConstant as Ints +private import semmle.code.cpp.models.interfaces.Alias + private class IntValue = Ints::IntValue; /** @@ -46,7 +48,7 @@ private IntValue getFieldBitOffset(Field field) { * not result in any address held in that operand from escaping beyond the * instruction. */ -predicate operandIsConsumedWithoutEscaping(Operand operand) { +private predicate operandIsConsumedWithoutEscaping(Operand operand) { // The source/destination address of a Load/Store does not escape (but the // loaded/stored value could). operand instanceof AddressOperand or @@ -60,7 +62,18 @@ predicate operandIsConsumedWithoutEscaping(Operand operand) { // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultType() instanceof BoolType ) - ) + ) or + // Some standard function arguments never escape + isNeverEscapesArgument(operand) +} + +private predicate operandEscapesDomain(Operand operand) { + not operandIsConsumedWithoutEscaping(operand) and + not operandIsPropagated(operand, _) and + not isArgumentForParameter(_, operand, _) and + not isOnlyEscapesViaReturnArgument(operand) and + not operand.getUseInstruction() instanceof ReturnValueInstruction and + not operand instanceof PhiOperand } /** @@ -98,7 +111,7 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { * `bitOffset`. If the address is propagated, but the offset is not known to be * a constant, then `bitOffset` is unknown. */ -predicate operandIsPropagated(Operand operand, IntValue bitOffset) { +private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { exists(Instruction instr | instr = operand.getUseInstruction() and ( @@ -134,34 +147,134 @@ predicate operandIsPropagated(Operand operand, IntValue bitOffset) { // offset of the field. bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 or + // Some functions are known to propagate an argument + isAlwaysReturnedArgument(operand) and bitOffset = 0 ) ) } -/** - * Holds if any address held in operand number `tag` of instruction `instr` - * escapes outside the domain of the analysis. - */ -predicate operandEscapes(Operand operand) { - // Conservatively assume that the address escapes unless one of the following - // holds: - not ( - // The operand is used in a way that does not escape the instruction - operandIsConsumedWithoutEscaping(operand) or - // The address is propagated to the result of the instruction, but that - // result does not itself escape. - operandIsPropagated(operand, _) and not resultEscapes(operand.getUseInstruction()) +private predicate operandEscapesNonReturn(Operand operand) { + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUseInstruction()) + or + // The operand is used in a function call which returns it, and the return value is then returned + exists(CallInstruction ci, Instruction init | + isArgumentForParameter(ci, operand, init) and + ( + resultMayReachReturn(init) and + resultEscapesNonReturn(ci) + or + resultEscapesNonReturn(init) + ) ) + or + isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUseInstruction()) + or + operand instanceof PhiOperand and + resultEscapesNonReturn(operand.getUseInstruction()) + or + operandEscapesDomain(operand) +} + +private predicate operandMayReachReturn(Operand operand) { + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _) and + resultMayReachReturn(operand.getUseInstruction()) + or + // The operand is used in a function call which returns it, and the return value is then returned + exists(CallInstruction ci, Instruction init | + isArgumentForParameter(ci, operand, init) and + resultMayReachReturn(init) and + resultMayReachReturn(ci) + ) + or + // The address is returned + operand.getUseInstruction() instanceof ReturnValueInstruction + or + isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUseInstruction()) + or + operand instanceof PhiOperand and + resultMayReachReturn(operand.getUseInstruction()) +} + +private predicate operandReturned(Operand operand, IntValue bitOffset) { + // The address is propagated to the result of the instruction, and that result itself is returned + exists(IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1) and + resultReturned(operand.getUseInstruction(), bitOffset2) and + bitOffset = Ints::add(bitOffset1, bitOffset2) + ) + or + // The operand is used in a function call which returns it, and the return value is then returned + exists(CallInstruction ci, Instruction init, IntValue bitOffset1, IntValue bitOffset2 | + isArgumentForParameter(ci, operand, init) and + resultReturned(init, bitOffset1) and + resultReturned(ci, bitOffset2) and + bitOffset = Ints::add(bitOffset1, bitOffset2) + + ) + or + // The address is returned + operand.getUseInstruction() instanceof ReturnValueInstruction and + bitOffset = 0 + or + isOnlyEscapesViaReturnArgument(operand) and resultReturned(operand.getUseInstruction(), _) and + bitOffset = Ints::unknown() +} + +private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { + exists(Function f | + ci = operand.getUseInstruction() and + f = ci.getStaticCallTarget() and + ( + init.(InitializeParameterInstruction).getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + or + init instanceof InitializeThisInstruction and + init.getEnclosingFunction() = f and + operand instanceof ThisArgumentOperand + ) and + not f.isVirtual() and + not f instanceof AliasFunction + ) +} + +private predicate isAlwaysReturnedArgument(Operand operand) { + exists(AliasFunction f | + f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and + f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) + ) +} + +private predicate isOnlyEscapesViaReturnArgument(Operand operand) { + exists(AliasFunction f | + f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and + f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + ) +} + +private predicate isNeverEscapesArgument(Operand operand) { + exists(AliasFunction f | + f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and + f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + ) +} + +private predicate resultReturned(Instruction instr, IntValue bitOffset) { + operandReturned(instr.getAUse(), bitOffset) +} + +private predicate resultMayReachReturn(Instruction instr) { + operandMayReachReturn(instr.getAUse()) } /** * Holds if any address held in the result of instruction `instr` escapes * outside the domain of the analysis. */ -predicate resultEscapes(Instruction instr) { +private predicate resultEscapesNonReturn(Instruction instr) { // The result escapes if it has at least one use that escapes. - operandEscapes(instr.getAUse()) + operandEscapesNonReturn(instr.getAUse()) } /** @@ -169,15 +282,11 @@ predicate resultEscapes(Instruction instr) { * domain of the analysis. */ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { - exists(FunctionIR funcIR | - funcIR = var.getEnclosingFunctionIR() and - // The variable's address escapes if the result of any - // VariableAddressInstruction that computes the variable's address escapes. - exists(VariableAddressInstruction instr | - instr.getEnclosingFunctionIR() = funcIR and - instr.getVariable() = var and - resultEscapes(instr) - ) + // The variable's address escapes if the result of any + // VariableAddressInstruction that computes the variable's address escapes. + exists(VariableAddressInstruction instr | + instr.getVariable() = var and + resultEscapesNonReturn(instr) ) } @@ -207,7 +316,14 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) // If an operand is propagated, then the result points to the same variable, // offset by the bit offset from the propagation. resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and - operandIsPropagated(operand, propagatedBitOffset) and + ( + operandIsPropagated(operand, propagatedBitOffset) + or + exists(CallInstruction ci, Instruction init | + isArgumentForParameter(ci, operand, init) and + resultReturned(init, propagatedBitOffset) + ) + ) and bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index fb25314f230..8335469612a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction { result = getAnOperand() } + /** + * Gets the `Function` that the call targets, if this is statically known. + */ + final Function getStaticCallTarget() { + result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + } + /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index fb25314f230..8335469612a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction { result = getAnOperand() } + /** + * Gets the `Function` that the call targets, if this is statically known. + */ + final Function getStaticCallTarget() { + result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + } + /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 7ad949a1a8c..0ccbcd381bb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -3,6 +3,8 @@ import cpp private import InputIR private import semmle.code.cpp.ir.internal.IntegerConstant as Ints +private import semmle.code.cpp.models.interfaces.Alias + private class IntValue = Ints::IntValue; /** @@ -46,7 +48,7 @@ private IntValue getFieldBitOffset(Field field) { * not result in any address held in that operand from escaping beyond the * instruction. */ -predicate operandIsConsumedWithoutEscaping(Operand operand) { +private predicate operandIsConsumedWithoutEscaping(Operand operand) { // The source/destination address of a Load/Store does not escape (but the // loaded/stored value could). operand instanceof AddressOperand or @@ -60,7 +62,18 @@ predicate operandIsConsumedWithoutEscaping(Operand operand) { // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultType() instanceof BoolType ) - ) + ) or + // Some standard function arguments never escape + isNeverEscapesArgument(operand) +} + +private predicate operandEscapesDomain(Operand operand) { + not operandIsConsumedWithoutEscaping(operand) and + not operandIsPropagated(operand, _) and + not isArgumentForParameter(_, operand, _) and + not isOnlyEscapesViaReturnArgument(operand) and + not operand.getUseInstruction() instanceof ReturnValueInstruction and + not operand instanceof PhiOperand } /** @@ -98,7 +111,7 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { * `bitOffset`. If the address is propagated, but the offset is not known to be * a constant, then `bitOffset` is unknown. */ -predicate operandIsPropagated(Operand operand, IntValue bitOffset) { +private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { exists(Instruction instr | instr = operand.getUseInstruction() and ( @@ -134,34 +147,134 @@ predicate operandIsPropagated(Operand operand, IntValue bitOffset) { // offset of the field. bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 or + // Some functions are known to propagate an argument + isAlwaysReturnedArgument(operand) and bitOffset = 0 ) ) } -/** - * Holds if any address held in operand number `tag` of instruction `instr` - * escapes outside the domain of the analysis. - */ -predicate operandEscapes(Operand operand) { - // Conservatively assume that the address escapes unless one of the following - // holds: - not ( - // The operand is used in a way that does not escape the instruction - operandIsConsumedWithoutEscaping(operand) or - // The address is propagated to the result of the instruction, but that - // result does not itself escape. - operandIsPropagated(operand, _) and not resultEscapes(operand.getUseInstruction()) +private predicate operandEscapesNonReturn(Operand operand) { + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUseInstruction()) + or + // The operand is used in a function call which returns it, and the return value is then returned + exists(CallInstruction ci, Instruction init | + isArgumentForParameter(ci, operand, init) and + ( + resultMayReachReturn(init) and + resultEscapesNonReturn(ci) + or + resultEscapesNonReturn(init) + ) ) + or + isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUseInstruction()) + or + operand instanceof PhiOperand and + resultEscapesNonReturn(operand.getUseInstruction()) + or + operandEscapesDomain(operand) +} + +private predicate operandMayReachReturn(Operand operand) { + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _) and + resultMayReachReturn(operand.getUseInstruction()) + or + // The operand is used in a function call which returns it, and the return value is then returned + exists(CallInstruction ci, Instruction init | + isArgumentForParameter(ci, operand, init) and + resultMayReachReturn(init) and + resultMayReachReturn(ci) + ) + or + // The address is returned + operand.getUseInstruction() instanceof ReturnValueInstruction + or + isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUseInstruction()) + or + operand instanceof PhiOperand and + resultMayReachReturn(operand.getUseInstruction()) +} + +private predicate operandReturned(Operand operand, IntValue bitOffset) { + // The address is propagated to the result of the instruction, and that result itself is returned + exists(IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1) and + resultReturned(operand.getUseInstruction(), bitOffset2) and + bitOffset = Ints::add(bitOffset1, bitOffset2) + ) + or + // The operand is used in a function call which returns it, and the return value is then returned + exists(CallInstruction ci, Instruction init, IntValue bitOffset1, IntValue bitOffset2 | + isArgumentForParameter(ci, operand, init) and + resultReturned(init, bitOffset1) and + resultReturned(ci, bitOffset2) and + bitOffset = Ints::add(bitOffset1, bitOffset2) + + ) + or + // The address is returned + operand.getUseInstruction() instanceof ReturnValueInstruction and + bitOffset = 0 + or + isOnlyEscapesViaReturnArgument(operand) and resultReturned(operand.getUseInstruction(), _) and + bitOffset = Ints::unknown() +} + +private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { + exists(Function f | + ci = operand.getUseInstruction() and + f = ci.getStaticCallTarget() and + ( + init.(InitializeParameterInstruction).getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + or + init instanceof InitializeThisInstruction and + init.getEnclosingFunction() = f and + operand instanceof ThisArgumentOperand + ) and + not f.isVirtual() and + not f instanceof AliasFunction + ) +} + +private predicate isAlwaysReturnedArgument(Operand operand) { + exists(AliasFunction f | + f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and + f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) + ) +} + +private predicate isOnlyEscapesViaReturnArgument(Operand operand) { + exists(AliasFunction f | + f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and + f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + ) +} + +private predicate isNeverEscapesArgument(Operand operand) { + exists(AliasFunction f | + f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and + f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + ) +} + +private predicate resultReturned(Instruction instr, IntValue bitOffset) { + operandReturned(instr.getAUse(), bitOffset) +} + +private predicate resultMayReachReturn(Instruction instr) { + operandMayReachReturn(instr.getAUse()) } /** * Holds if any address held in the result of instruction `instr` escapes * outside the domain of the analysis. */ -predicate resultEscapes(Instruction instr) { +private predicate resultEscapesNonReturn(Instruction instr) { // The result escapes if it has at least one use that escapes. - operandEscapes(instr.getAUse()) + operandEscapesNonReturn(instr.getAUse()) } /** @@ -169,15 +282,11 @@ predicate resultEscapes(Instruction instr) { * domain of the analysis. */ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { - exists(FunctionIR funcIR | - funcIR = var.getEnclosingFunctionIR() and - // The variable's address escapes if the result of any - // VariableAddressInstruction that computes the variable's address escapes. - exists(VariableAddressInstruction instr | - instr.getEnclosingFunctionIR() = funcIR and - instr.getVariable() = var and - resultEscapes(instr) - ) + // The variable's address escapes if the result of any + // VariableAddressInstruction that computes the variable's address escapes. + exists(VariableAddressInstruction instr | + instr.getVariable() = var and + resultEscapesNonReturn(instr) ) } @@ -207,7 +316,14 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) // If an operand is propagated, then the result points to the same variable, // offset by the bit offset from the propagation. resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and - operandIsPropagated(operand, propagatedBitOffset) and + ( + operandIsPropagated(operand, propagatedBitOffset) + or + exists(CallInstruction ci, Instruction init | + isArgumentForParameter(ci, operand, init) and + resultReturned(init, propagatedBitOffset) + ) + ) and bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) ) } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected index 1eccb0fedf1..f0fc017b75c 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected @@ -1,4 +1,3 @@ -| test.cpp:66:30:66:36 | test.cpp:71:8:71:9 | AST only | | test.cpp:89:28:89:34 | test.cpp:92:8:92:14 | IR only | | test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only | | test.cpp:109:9:109:14 | test.cpp:110:10:110:12 | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected index 5e3d4970bd7..069a5099c4f 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected @@ -6,6 +6,7 @@ | test.cpp:30:8:30:8 | Load: t | test.cpp:35:10:35:15 | Call: call to source | | test.cpp:31:8:31:8 | Load: c | test.cpp:36:13:36:18 | Call: call to source | | test.cpp:58:10:58:10 | Load: t | test.cpp:50:14:50:19 | Call: call to source | +| test.cpp:71:8:71:9 | Load: x4 | test.cpp:66:30:66:36 | InitializeParameter: source1 | | test.cpp:76:8:76:9 | Load: u1 | test.cpp:75:7:75:8 | Uninitialized: definition of u1 | | test.cpp:84:8:84:18 | Load: ... ? ... : ... | test.cpp:83:7:83:8 | Uninitialized: definition of u2 | | test.cpp:86:8:86:9 | Load: i1 | test.cpp:83:7:83:8 | Uninitialized: definition of u2 | diff --git a/cpp/ql/test/library-tests/ir/escape/escape.cpp b/cpp/ql/test/library-tests/ir/escape/escape.cpp index 8a3e57a25d2..d1f7641e3b8 100644 --- a/cpp/ql/test/library-tests/ir/escape/escape.cpp +++ b/cpp/ql/test/library-tests/ir/escape/escape.cpp @@ -1,5 +1,44 @@ void CallByPointer(int* p); void CallByReference(int& r); +int *GetPointer(); +int &GetReference(); + +int FetchFromPointer(int *no_p) { + return *no_p; +} + +int FetchFromReference(int &no_r) { + return no_r; +} + +int *ReturnPointer(int *no_p) { + return no_p; +} + +int &ReturnReference(int &no_r) { + return no_r; +} + +void CallByPointerParamEscape(int *no_p) { + CallByPointer(no_p); +} + +void CallByReferenceParamEscape(int &no_r) { + CallByReference(no_r); +} + +int *MaybeReturn(int *no_p, int *no_q, bool no_b) { + if (no_b) { + return no_p; + } else { + return no_q; + } +} + +int &EscapeAndReturn(int &no_r) { + CallByReference(no_r); + return no_r; +} struct Point { float x; @@ -27,6 +66,40 @@ struct Derived : Intermediate1, Intermediate2 { float d; }; +class C; + +void CEscapes(C *no_c); + +class C { +public: + void ThisEscapes() { + CEscapes(this); + } + + C *ThisReturned() { + return this; + } + + virtual C *Overridden() { + CEscapes(this); + return nullptr; + } +}; + +class OverrideReturns : C { +public: + virtual C *Overridden() { + return this; + } +}; + +class OverrideNone : C { +public: + virtual C *Overridden() { + return nullptr; + } +}; + void Escape() { int no_result; @@ -95,4 +168,86 @@ void Escape() int passByRef; CallByReference(passByRef); + + int no_ssa_passByPtr; + FetchFromPointer(&no_ssa_passByPtr); + + int no_ssa_passByRef; + FetchFromReference(no_ssa_passByRef); + + int no_ssa_passByPtr_ret; + FetchFromPointer(&no_ssa_passByPtr_ret); + + int no_ssa_passByRef_ret; + FetchFromReference(no_ssa_passByRef_ret); + + int passByPtr2; + CallByPointerParamEscape(&passByPtr2); + + int passByRef2; + CallByReferenceParamEscape(passByRef2); + + int passByPtr3; + CallByPointerParamEscape(ReturnPointer(&passByPtr3)); + + int passByRef3; + CallByReferenceParamEscape(ReturnReference(passByRef3)); + + int no_ssa_passByPtr4; + int no_ssa_passByPtr5; + bool no_b2 = false; + MaybeReturn(&no_ssa_passByPtr4, &no_ssa_passByPtr5, no_b2); + + int passByRef6; + EscapeAndReturn(passByRef6); + + int no_ssa_passByRef7; + ReturnReference(no_ssa_passByRef7); + + C no_ssa_c; + + no_ssa_c.ThisReturned(); + + C c; + + c.ThisEscapes(); + + C c2; + + CEscapes(&c2); + + C c3; + + c3.ThisReturned()->ThisEscapes(); + + C c4; + + CEscapes(c4.ThisReturned()); + + C c5; + + c5.Overridden(); + + OverrideReturns or1; + or1.Overridden(); + + OverrideReturns or2; + CEscapes(or2.Overridden()); + + OverrideNone on1; + on1.Overridden(); + + OverrideNone on2; + CEscapes(on2.Overridden()); + + int condEscape1, condEscape2; + + int *no_condTemp; + if(GetPointer()) { + no_condTemp = &condEscape1; + } else { + no_condTemp = &condEscape2; + } + CallByPointer(no_condTemp); } + diff --git a/cpp/ql/test/library-tests/ir/escape/points_to.expected b/cpp/ql/test/library-tests/ir/escape/points_to.expected index 5d387a4b267..f40460f4d91 100644 --- a/cpp/ql/test/library-tests/ir/escape/points_to.expected +++ b/cpp/ql/test/library-tests/ir/escape/points_to.expected @@ -1,108 +1,47 @@ -| escape.cpp:32:9:32:17 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:33:9:33:11 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:35:5:35:7 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:36:5:36:7 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:36:11:36:13 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:37:5:37:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:37:17:37:19 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:38:5:38:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:38:19:38:21 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:40:5:40:7 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:41:6:41:8 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:42:5:42:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:42:19:42:28 | PointerAdd[4] | no_+0:0 | -| escape.cpp:42:21:42:23 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:43:5:43:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:43:19:43:28 | PointerSub[4] | no_+0:0 | -| escape.cpp:43:21:43:23 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:44:5:44:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:44:19:44:26 | PointerAdd[4] | no_+0:0 | -| escape.cpp:44:24:44:26 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:45:10:45:12 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:47:13:47:15 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:50:15:50:17 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:51:10:51:12 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:51:16:51:18 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:51:22:51:24 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:54:10:54:12 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:56:13:56:15 | VariableAddress[no_] | no_+0:0 | -| escape.cpp:59:9:59:16 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:60:5:60:12 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:61:5:61:18 | Convert | no_Array+0:0 | -| escape.cpp:61:11:61:18 | Convert | no_Array+0:0 | -| escape.cpp:61:11:61:18 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:62:5:62:12 | Convert | no_Array+0:0 | -| escape.cpp:62:5:62:12 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:62:5:62:15 | PointerAdd[4] | no_Array+20:0 | -| escape.cpp:63:5:63:15 | PointerAdd[4] | no_Array+20:0 | -| escape.cpp:63:7:63:14 | Convert | no_Array+0:0 | -| escape.cpp:63:7:63:14 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:64:5:64:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:64:17:64:24 | Convert | no_Array+0:0 | -| escape.cpp:64:17:64:24 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:64:17:64:27 | PointerAdd[4] | no_Array+20:0 | -| escape.cpp:65:5:65:13 | VariableAddress[no_result] | no_result+0:0 | -| escape.cpp:65:17:65:27 | PointerAdd[4] | no_Array+20:0 | -| escape.cpp:65:19:65:26 | Convert | no_Array+0:0 | -| escape.cpp:65:19:65:26 | VariableAddress[no_Array] | no_Array+0:0 | -| escape.cpp:67:11:67:18 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:67:21:67:32 | FieldAddress[x] | no_Point+0:0 | -| escape.cpp:67:21:67:32 | FieldAddress[y] | no_Point+4:0 | -| escape.cpp:67:21:67:32 | FieldAddress[z] | no_Point+8:0 | -| escape.cpp:68:11:68:14 | VariableAddress[no_x] | no_x+0:0 | -| escape.cpp:68:18:68:25 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:68:27:68:27 | FieldAddress[x] | no_Point+0:0 | -| escape.cpp:69:5:69:12 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:69:14:69:14 | FieldAddress[y] | no_Point+4:0 | -| escape.cpp:69:18:69:21 | VariableAddress[no_x] | no_x+0:0 | -| escape.cpp:70:11:70:14 | VariableAddress[no_y] | no_y+0:0 | -| escape.cpp:70:20:70:27 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:70:31:70:31 | FieldAddress[y] | no_Point+4:0 | -| escape.cpp:71:7:71:14 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:71:18:71:18 | FieldAddress[y] | no_Point+4:0 | -| escape.cpp:71:22:71:25 | VariableAddress[no_y] | no_y+0:0 | -| escape.cpp:72:11:72:14 | VariableAddress[no_z] | no_z+0:0 | -| escape.cpp:72:21:72:28 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:72:30:72:30 | FieldAddress[z] | no_Point+8:0 | -| escape.cpp:73:8:73:15 | VariableAddress[no_Point] | no_Point+0:0 | -| escape.cpp:73:17:73:17 | FieldAddress[z] | no_Point+8:0 | -| escape.cpp:73:22:73:25 | VariableAddress[no_z] | no_z+0:0 | -| escape.cpp:75:13:75:22 | VariableAddress[no_Derived] | no_Derived+0:0 | -| escape.cpp:76:5:76:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | -| escape.cpp:76:5:76:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | -| escape.cpp:76:5:76:14 | VariableAddress[no_Derived] | no_Derived+0:0 | -| escape.cpp:76:16:76:16 | FieldAddress[b] | no_Derived+0:0 | -| escape.cpp:77:11:77:14 | VariableAddress[no_b] | no_b+0:0 | -| escape.cpp:77:18:77:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | -| escape.cpp:77:18:77:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | -| escape.cpp:77:18:77:27 | VariableAddress[no_Derived] | no_Derived+0:0 | -| escape.cpp:77:29:77:29 | FieldAddress[b] | no_Derived+0:0 | -| escape.cpp:78:5:78:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | -| escape.cpp:78:5:78:14 | VariableAddress[no_Derived] | no_Derived+0:0 | -| escape.cpp:78:16:78:17 | FieldAddress[i2] | no_Derived+16:0 | -| escape.cpp:79:11:79:15 | VariableAddress[no_i2] | no_i2+0:0 | -| escape.cpp:79:19:79:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | -| escape.cpp:79:19:79:28 | VariableAddress[no_Derived] | no_Derived+0:0 | -| escape.cpp:79:30:79:31 | FieldAddress[i2] | no_Derived+16:0 | -| escape.cpp:81:9:81:21 | VariableAddress[no_ssa_addrOf] | no_ssa_addrOf+0:0 | -| escape.cpp:82:10:82:13 | VariableAddress[no_p] | no_p+0:0 | -| escape.cpp:82:17:82:30 | Store | no_ssa_addrOf+0:0 | -| escape.cpp:82:18:82:30 | VariableAddress[no_ssa_addrOf] | no_ssa_addrOf+0:0 | -| escape.cpp:84:9:84:20 | VariableAddress[no_ssa_refTo] | no_ssa_refTo+0:0 | -| escape.cpp:85:10:85:13 | VariableAddress[no_r] | no_r+0:0 | -| escape.cpp:85:17:85:28 | Store | no_ssa_refTo+0:0 | -| escape.cpp:85:17:85:28 | VariableAddress[no_ssa_refTo] | no_ssa_refTo+0:0 | -| escape.cpp:87:9:87:32 | VariableAddress[no_ssa_refToArrayElement] | no_ssa_refToArrayElement+0:0 | -| escape.cpp:88:10:88:15 | VariableAddress[no_rae] | no_rae+0:0 | -| escape.cpp:88:19:88:42 | Convert | no_ssa_refToArrayElement+0:0 | -| escape.cpp:88:19:88:42 | VariableAddress[no_ssa_refToArrayElement] | no_ssa_refToArrayElement+0:0 | -| escape.cpp:88:19:88:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 | -| escape.cpp:88:19:88:45 | Store | no_ssa_refToArrayElement+20:0 | -| escape.cpp:90:9:90:25 | VariableAddress[no_ssa_refToArray] | no_ssa_refToArray+0:0 | -| escape.cpp:91:11:91:15 | VariableAddress[no_ra] | no_ra+0:0 | -| escape.cpp:91:24:91:40 | Store | no_ssa_refToArray+0:0 | -| escape.cpp:91:24:91:40 | VariableAddress[no_ssa_refToArray] | no_ssa_refToArray+0:0 | -| escape.cpp:93:9:93:17 | VariableAddress[passByPtr] | passByPtr+0:0 | -| escape.cpp:94:20:94:28 | VariableAddress[passByPtr] | passByPtr+0:0 | -| escape.cpp:96:9:96:17 | VariableAddress[passByRef] | passByRef+0:0 | -| escape.cpp:97:21:97:29 | VariableAddress[passByRef] | passByRef+0:0 | +| escape.cpp:115:19:115:28 | PointerAdd[4] | no_+0:0 | no_+0:0 | +| escape.cpp:116:19:116:28 | PointerSub[4] | no_+0:0 | no_+0:0 | +| escape.cpp:117:19:117:26 | PointerAdd[4] | no_+0:0 | no_+0:0 | +| escape.cpp:134:5:134:18 | Convert | no_Array+0:0 | no_Array+0:0 | +| escape.cpp:134:11:134:18 | Convert | no_Array+0:0 | no_Array+0:0 | +| escape.cpp:135:5:135:12 | Convert | no_Array+0:0 | no_Array+0:0 | +| escape.cpp:135:5:135:15 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | +| escape.cpp:136:5:136:15 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | +| escape.cpp:136:7:136:14 | Convert | no_Array+0:0 | no_Array+0:0 | +| escape.cpp:137:17:137:24 | Convert | no_Array+0:0 | no_Array+0:0 | +| escape.cpp:137:17:137:27 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | +| escape.cpp:138:17:138:27 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | +| escape.cpp:138:19:138:26 | Convert | no_Array+0:0 | no_Array+0:0 | +| escape.cpp:140:21:140:32 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 | +| escape.cpp:140:21:140:32 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | +| escape.cpp:140:21:140:32 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 | +| escape.cpp:141:27:141:27 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 | +| escape.cpp:142:14:142:14 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | +| escape.cpp:143:31:143:31 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | +| escape.cpp:144:18:144:18 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | +| escape.cpp:145:30:145:30 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 | +| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 | +| escape.cpp:149:5:149:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 | +| escape.cpp:149:5:149:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 | +| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 | +| escape.cpp:150:18:150:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 | +| escape.cpp:150:18:150:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 | +| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 | +| escape.cpp:151:5:151:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 | +| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 | +| escape.cpp:152:19:152:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 | +| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 | +| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 | +| escape.cpp:158:17:158:28 | Store | no_ssa_refTo+0:0 | no_ssa_refTo+0:0 | +| escape.cpp:161:19:161:42 | Convert | no_ssa_refToArrayElement+0:0 | no_ssa_refToArrayElement+0:0 | +| escape.cpp:161:19:161:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 | +| escape.cpp:161:19:161:45 | Store | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 | +| escape.cpp:164:24:164:40 | Store | no_ssa_refToArray+0:0 | no_ssa_refToArray+0:0 | +| escape.cpp:191:30:191:42 | Call | none | passByPtr3+0:0 | +| escape.cpp:194:32:194:46 | Call | none | passByRef3+0:0 | +| escape.cpp:202:5:202:19 | Call | none | passByRef6+0:0 | +| escape.cpp:205:5:205:19 | Call | none | no_ssa_passByRef7+0:0 | +| escape.cpp:209:14:209:25 | Call | none | no_ssa_c+0:0 | +| escape.cpp:221:8:221:19 | Call | none | c3+0:0 | +| escape.cpp:225:17:225:28 | Call | none | c4+0:0 | +| escape.cpp:247:2:247:27 | Store | condEscape1+0:0 | condEscape1+0:0 | +| escape.cpp:249:9:249:34 | Store | condEscape2+0:0 | condEscape2+0:0 | diff --git a/cpp/ql/test/library-tests/ir/escape/points_to.ql b/cpp/ql/test/library-tests/ir/escape/points_to.ql index 06b5e746853..04dbf0e0060 100644 --- a/cpp/ql/test/library-tests/ir/escape/points_to.ql +++ b/cpp/ql/test/library-tests/ir/escape/points_to.ql @@ -1,11 +1,34 @@ import default -import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis -import semmle.code.cpp.ir.implementation.raw.IR +import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis as RawAA +import semmle.code.cpp.ir.implementation.raw.IR as Raw +import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis as UnAA +import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Un +import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction -from Instruction instr, string pointsTo +from Raw::Instruction rawInstr, Un::Instruction unInstr, string rawPointsTo, string unPointsTo where - exists(IRVariable var, int bitOffset | - resultPointsTo(instr, var, bitOffset) and - pointsTo = var.toString() + getBitOffsetString(bitOffset) + rawInstr = getOldInstruction(unInstr) and + not rawInstr instanceof Raw::VariableAddressInstruction and + ( + exists(Variable var, int rawBitOffset, int unBitOffset | + RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and + rawPointsTo = var.toString() + RawAA::getBitOffsetString(rawBitOffset) and + UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and + unPointsTo = var.toString() + UnAA::getBitOffsetString(unBitOffset) + ) + or + exists(Variable var, int unBitOffset | + not RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), _) and + rawPointsTo = "none" and + UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and + unPointsTo = var.toString() + UnAA::getBitOffsetString(unBitOffset) + ) + or + exists(Variable var, int rawBitOffset | + RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and + rawPointsTo = var.toString() + RawAA::getBitOffsetString(rawBitOffset) and + not UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), _) and + unPointsTo = "none" + ) ) -select instr.getLocation().toString(), instr.getOperationString(), pointsTo +select rawInstr.getLocation().toString(), rawInstr.getOperationString(), rawPointsTo, unPointsTo