Skip to content

Commit a447b04

Browse files
author
Dave Bartolomeo
committed
C++: Impoved alias analysis of smart pointers
1 parent 63fe4fb commit a447b04

File tree

7 files changed

+386
-64
lines changed

7 files changed

+386
-64
lines changed

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,71 @@ private import AliasAnalysisImports
44

55
private class IntValue = Ints::IntValue;
66

7+
/**
8+
* If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side
9+
* effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`.
10+
*/
11+
private CallInstruction getPrimaryCall(Instruction instr) {
12+
result = instr.(CallInstruction)
13+
or
14+
result = instr.(SideEffectInstruction).getPrimaryInstruction()
15+
}
16+
17+
/**
18+
* Holds if `operand` serves as an input argument (or indirection) to `call`, in the position
19+
* specified by `input`.
20+
*/
21+
private predicate isCallInput(
22+
CallInstruction call, Operand operand, AliasModels::FunctionInput input
23+
) {
24+
call = getPrimaryCall(operand.getUse()) and
25+
(
26+
exists(int index |
27+
input.isParameterOrQualifierAddress(index) and
28+
operand = call.getArgumentOperand(index)
29+
)
30+
or
31+
exists(int index, ReadSideEffectInstruction read |
32+
input.isParameterDerefOrQualifierObject(index) and
33+
read = call.getAParameterSideEffect(index) and
34+
operand = read.getSideEffectOperand()
35+
)
36+
)
37+
}
38+
39+
/**
40+
* Holds if `instr` serves as a return value or output argument indirection for `call`, in the
41+
* position specified by `output`.
42+
*/
43+
private predicate isCallOutput(
44+
CallInstruction call, Instruction instr, AliasModels::FunctionOutput output
45+
) {
46+
call = getPrimaryCall(instr) and
47+
(
48+
output.isReturnValue() and instr = call
49+
or
50+
exists(int index, WriteSideEffectInstruction write |
51+
output.isParameterDerefOrQualifierObject(index) and
52+
write = call.getAParameterSideEffect(index) and
53+
instr = write
54+
)
55+
)
56+
}
57+
58+
/**
59+
* Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled
60+
* address flow through a function call.
61+
*/
62+
private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) {
63+
exists(
64+
CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output
65+
|
66+
call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and
67+
isCallInput(call, operand, input) and
68+
isCallOutput(call, resultInstr, output)
69+
)
70+
}
71+
772
/**
873
* Holds if the operand `tag` of instruction `instr` is used in a way that does
974
* not result in any address held in that operand from escaping beyond the
@@ -74,6 +139,10 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
74139
* be a constant, then `bitOffset` is `unknown()`.
75140
*/
76141
private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) {
142+
// Some functions are known to propagate an argument
143+
hasAddressFlowThroughCall(operand, instr) and
144+
bitOffset = 0
145+
or
77146
instr = operand.getUse() and
78147
(
79148
// Converting to a non-virtual base class adds the offset of the base class.
@@ -118,9 +187,6 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instr
118187
or
119188
// A copy propagates the source value.
120189
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
121-
or
122-
// Some functions are known to propagate an argument
123-
isAlwaysReturnedArgument(operand) and bitOffset = 0
124190
)
125191
}
126192

@@ -215,13 +281,6 @@ private predicate isArgumentForParameter(
215281
)
216282
}
217283

218-
private predicate isAlwaysReturnedArgument(Operand operand) {
219-
exists(AliasModels::AliasFunction f |
220-
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
221-
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
222-
)
223-
}
224-
225284
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
226285
exists(AliasModels::AliasFunction f |
227286
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
@@ -271,7 +330,9 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
271330
/**
272331
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
273332
*/
274-
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset, Instruction instr) {
333+
private predicate operandIsPropagatedIncludingByCall(
334+
Operand operand, IntValue bitOffset, Instruction instr
335+
) {
275336
operandIsPropagated(operand, bitOffset, instr)
276337
or
277338
exists(CallInstruction call, Instruction init |

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,21 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
105105
DynamicAllocation() { this = TDynamicAllocation(call) }
106106

107107
final override string toString() {
108-
result = call.toString() + " at " + call.getLocation() // This isn't performant, but it's only used in test/dump code right now.
108+
// This isn't performant, but it's only used in test/dump code right now.
109+
// Dynamic allocations within a function are numbered in the order by start
110+
// line number. This keeps them stable when the function moves within the
111+
// file, or when non-allocating lines are added and removed within the
112+
// function.
113+
exists(int i |
114+
result = "dynamic{" + i.toString() + "}" and
115+
call =
116+
rank[i](CallInstruction rangeCall |
117+
exists(TDynamicAllocation(rangeCall)) and
118+
rangeCall.getEnclosingIRFunction() = call.getEnclosingIRFunction()
119+
|
120+
rangeCall order by rangeCall.getLocation().getStartLine()
121+
)
122+
)
109123
}
110124

111125
final override CallInstruction getABaseInstruction() { result = call }

cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,71 @@ private import AliasAnalysisImports
44

55
private class IntValue = Ints::IntValue;
66

7+
/**
8+
* If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side
9+
* effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`.
10+
*/
11+
private CallInstruction getPrimaryCall(Instruction instr) {
12+
result = instr.(CallInstruction)
13+
or
14+
result = instr.(SideEffectInstruction).getPrimaryInstruction()
15+
}
16+
17+
/**
18+
* Holds if `operand` serves as an input argument (or indirection) to `call`, in the position
19+
* specified by `input`.
20+
*/
21+
private predicate isCallInput(
22+
CallInstruction call, Operand operand, AliasModels::FunctionInput input
23+
) {
24+
call = getPrimaryCall(operand.getUse()) and
25+
(
26+
exists(int index |
27+
input.isParameterOrQualifierAddress(index) and
28+
operand = call.getArgumentOperand(index)
29+
)
30+
or
31+
exists(int index, ReadSideEffectInstruction read |
32+
input.isParameterDerefOrQualifierObject(index) and
33+
read = call.getAParameterSideEffect(index) and
34+
operand = read.getSideEffectOperand()
35+
)
36+
)
37+
}
38+
39+
/**
40+
* Holds if `instr` serves as a return value or output argument indirection for `call`, in the
41+
* position specified by `output`.
42+
*/
43+
private predicate isCallOutput(
44+
CallInstruction call, Instruction instr, AliasModels::FunctionOutput output
45+
) {
46+
call = getPrimaryCall(instr) and
47+
(
48+
output.isReturnValue() and instr = call
49+
or
50+
exists(int index, WriteSideEffectInstruction write |
51+
output.isParameterDerefOrQualifierObject(index) and
52+
write = call.getAParameterSideEffect(index) and
53+
instr = write
54+
)
55+
)
56+
}
57+
58+
/**
59+
* Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled
60+
* address flow through a function call.
61+
*/
62+
private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) {
63+
exists(
64+
CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output
65+
|
66+
call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and
67+
isCallInput(call, operand, input) and
68+
isCallOutput(call, resultInstr, output)
69+
)
70+
}
71+
772
/**
873
* Holds if the operand `tag` of instruction `instr` is used in a way that does
974
* not result in any address held in that operand from escaping beyond the
@@ -74,6 +139,10 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
74139
* be a constant, then `bitOffset` is `unknown()`.
75140
*/
76141
private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) {
142+
// Some functions are known to propagate an argument
143+
hasAddressFlowThroughCall(operand, instr) and
144+
bitOffset = 0
145+
or
77146
instr = operand.getUse() and
78147
(
79148
// Converting to a non-virtual base class adds the offset of the base class.
@@ -118,9 +187,6 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instr
118187
or
119188
// A copy propagates the source value.
120189
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
121-
or
122-
// Some functions are known to propagate an argument
123-
isAlwaysReturnedArgument(operand) and bitOffset = 0
124190
)
125191
}
126192

@@ -215,13 +281,6 @@ private predicate isArgumentForParameter(
215281
)
216282
}
217283

218-
private predicate isAlwaysReturnedArgument(Operand operand) {
219-
exists(AliasModels::AliasFunction f |
220-
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
221-
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
222-
)
223-
}
224-
225284
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
226285
exists(AliasModels::AliasFunction f |
227286
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
@@ -271,7 +330,9 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
271330
/**
272331
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
273332
*/
274-
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset, Instruction instr) {
333+
private predicate operandIsPropagatedIncludingByCall(
334+
Operand operand, IntValue bitOffset, Instruction instr
335+
) {
275336
operandIsPropagated(operand, bitOffset, instr)
276337
or
277338
exists(CallInstruction call, Instruction init |

0 commit comments

Comments
 (0)