Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit db104ed

Browse files
authored
Merge pull request #792 from rdmarsh2/rdmarsh/cpp/escape-analysis
C++: Interprocedural escape/alias analysis in IR construction
2 parents f6828fa + 07bc9ca commit db104ed

10 files changed

Lines changed: 544 additions & 174 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction {
13101310
result = getAnOperand()
13111311
}
13121312

1313+
/**
1314+
* Gets the `Function` that the call targets, if this is statically known.
1315+
*/
1316+
final Function getStaticCallTarget() {
1317+
result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
1318+
}
1319+
13131320
/**
13141321
* Gets all of the arguments of the call, including the `this` pointer, if any.
13151322
*/

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

Lines changed: 145 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import cpp
33
private import InputIR
44
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
55

6+
private import semmle.code.cpp.models.interfaces.Alias
7+
68
private class IntValue = Ints::IntValue;
79

810
/**
@@ -46,7 +48,7 @@ private IntValue getFieldBitOffset(Field field) {
4648
* not result in any address held in that operand from escaping beyond the
4749
* instruction.
4850
*/
49-
predicate operandIsConsumedWithoutEscaping(Operand operand) {
51+
private predicate operandIsConsumedWithoutEscaping(Operand operand) {
5052
// The source/destination address of a Load/Store does not escape (but the
5153
// loaded/stored value could).
5254
operand instanceof AddressOperand or
@@ -60,7 +62,18 @@ predicate operandIsConsumedWithoutEscaping(Operand operand) {
6062
// Converting an address to a `bool` does not escape the address.
6163
instr.(ConvertInstruction).getResultType() instanceof BoolType
6264
)
63-
)
65+
) or
66+
// Some standard function arguments never escape
67+
isNeverEscapesArgument(operand)
68+
}
69+
70+
private predicate operandEscapesDomain(Operand operand) {
71+
not operandIsConsumedWithoutEscaping(operand) and
72+
not operandIsPropagated(operand, _) and
73+
not isArgumentForParameter(_, operand, _) and
74+
not isOnlyEscapesViaReturnArgument(operand) and
75+
not operand.getUseInstruction() instanceof ReturnValueInstruction and
76+
not operand instanceof PhiOperand
6477
}
6578

6679
/**
@@ -98,7 +111,7 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
98111
* `bitOffset`. If the address is propagated, but the offset is not known to be
99112
* a constant, then `bitOffset` is unknown.
100113
*/
101-
predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
114+
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
102115
exists(Instruction instr |
103116
instr = operand.getUseInstruction() and
104117
(
@@ -134,50 +147,146 @@ predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
134147
// offset of the field.
135148
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
136149
// A copy propagates the source value.
137-
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
150+
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 or
151+
// Some functions are known to propagate an argument
152+
isAlwaysReturnedArgument(operand) and bitOffset = 0
138153
)
139154
)
140155
}
141156

142-
/**
143-
* Holds if any address held in operand number `tag` of instruction `instr`
144-
* escapes outside the domain of the analysis.
145-
*/
146-
predicate operandEscapes(Operand operand) {
147-
// Conservatively assume that the address escapes unless one of the following
148-
// holds:
149-
not (
150-
// The operand is used in a way that does not escape the instruction
151-
operandIsConsumedWithoutEscaping(operand) or
152-
// The address is propagated to the result of the instruction, but that
153-
// result does not itself escape.
154-
operandIsPropagated(operand, _) and not resultEscapes(operand.getUseInstruction())
157+
private predicate operandEscapesNonReturn(Operand operand) {
158+
// The address is propagated to the result of the instruction, and that result itself is returned
159+
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUseInstruction())
160+
or
161+
// The operand is used in a function call which returns it, and the return value is then returned
162+
exists(CallInstruction ci, Instruction init |
163+
isArgumentForParameter(ci, operand, init) and
164+
(
165+
resultMayReachReturn(init) and
166+
resultEscapesNonReturn(ci)
167+
or
168+
resultEscapesNonReturn(init)
169+
)
155170
)
171+
or
172+
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUseInstruction())
173+
or
174+
operand instanceof PhiOperand and
175+
resultEscapesNonReturn(operand.getUseInstruction())
176+
or
177+
operandEscapesDomain(operand)
178+
}
179+
180+
private predicate operandMayReachReturn(Operand operand) {
181+
// The address is propagated to the result of the instruction, and that result itself is returned
182+
operandIsPropagated(operand, _) and
183+
resultMayReachReturn(operand.getUseInstruction())
184+
or
185+
// The operand is used in a function call which returns it, and the return value is then returned
186+
exists(CallInstruction ci, Instruction init |
187+
isArgumentForParameter(ci, operand, init) and
188+
resultMayReachReturn(init) and
189+
resultMayReachReturn(ci)
190+
)
191+
or
192+
// The address is returned
193+
operand.getUseInstruction() instanceof ReturnValueInstruction
194+
or
195+
isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUseInstruction())
196+
or
197+
operand instanceof PhiOperand and
198+
resultMayReachReturn(operand.getUseInstruction())
199+
}
200+
201+
private predicate operandReturned(Operand operand, IntValue bitOffset) {
202+
// The address is propagated to the result of the instruction, and that result itself is returned
203+
exists(IntValue bitOffset1, IntValue bitOffset2 |
204+
operandIsPropagated(operand, bitOffset1) and
205+
resultReturned(operand.getUseInstruction(), bitOffset2) and
206+
bitOffset = Ints::add(bitOffset1, bitOffset2)
207+
)
208+
or
209+
// The operand is used in a function call which returns it, and the return value is then returned
210+
exists(CallInstruction ci, Instruction init, IntValue bitOffset1, IntValue bitOffset2 |
211+
isArgumentForParameter(ci, operand, init) and
212+
resultReturned(init, bitOffset1) and
213+
resultReturned(ci, bitOffset2) and
214+
bitOffset = Ints::add(bitOffset1, bitOffset2)
215+
216+
)
217+
or
218+
// The address is returned
219+
operand.getUseInstruction() instanceof ReturnValueInstruction and
220+
bitOffset = 0
221+
or
222+
isOnlyEscapesViaReturnArgument(operand) and resultReturned(operand.getUseInstruction(), _) and
223+
bitOffset = Ints::unknown()
224+
}
225+
226+
private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
227+
exists(Function f |
228+
ci = operand.getUseInstruction() and
229+
f = ci.getStaticCallTarget() and
230+
(
231+
init.(InitializeParameterInstruction).getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
232+
or
233+
init instanceof InitializeThisInstruction and
234+
init.getEnclosingFunction() = f and
235+
operand instanceof ThisArgumentOperand
236+
) and
237+
not f.isVirtual() and
238+
not f instanceof AliasFunction
239+
)
240+
}
241+
242+
private predicate isAlwaysReturnedArgument(Operand operand) {
243+
exists(AliasFunction f |
244+
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
245+
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
246+
)
247+
}
248+
249+
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
250+
exists(AliasFunction f |
251+
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
252+
f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
253+
)
254+
}
255+
256+
private predicate isNeverEscapesArgument(Operand operand) {
257+
exists(AliasFunction f |
258+
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
259+
f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
260+
)
261+
}
262+
263+
private predicate resultReturned(Instruction instr, IntValue bitOffset) {
264+
operandReturned(instr.getAUse(), bitOffset)
265+
}
266+
267+
private predicate resultMayReachReturn(Instruction instr) {
268+
operandMayReachReturn(instr.getAUse())
156269
}
157270

158271
/**
159272
* Holds if any address held in the result of instruction `instr` escapes
160273
* outside the domain of the analysis.
161274
*/
162-
predicate resultEscapes(Instruction instr) {
275+
private predicate resultEscapesNonReturn(Instruction instr) {
163276
// The result escapes if it has at least one use that escapes.
164-
operandEscapes(instr.getAUse())
277+
operandEscapesNonReturn(instr.getAUse())
165278
}
166279

167280
/**
168281
* Holds if the address of the specified local variable or parameter escapes the
169282
* domain of the analysis.
170283
*/
171284
private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
172-
exists(FunctionIR funcIR |
173-
funcIR = var.getEnclosingFunctionIR() and
174-
// The variable's address escapes if the result of any
175-
// VariableAddressInstruction that computes the variable's address escapes.
176-
exists(VariableAddressInstruction instr |
177-
instr.getEnclosingFunctionIR() = funcIR and
178-
instr.getVariable() = var and
179-
resultEscapes(instr)
180-
)
285+
// The variable's address escapes if the result of any
286+
// VariableAddressInstruction that computes the variable's address escapes.
287+
exists(VariableAddressInstruction instr |
288+
instr.getVariable() = var and
289+
resultEscapesNonReturn(instr)
181290
)
182291
}
183292

@@ -207,7 +316,14 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
207316
// If an operand is propagated, then the result points to the same variable,
208317
// offset by the bit offset from the propagation.
209318
resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and
210-
operandIsPropagated(operand, propagatedBitOffset) and
319+
(
320+
operandIsPropagated(operand, propagatedBitOffset)
321+
or
322+
exists(CallInstruction ci, Instruction init |
323+
isArgumentForParameter(ci, operand, init) and
324+
resultReturned(init, propagatedBitOffset)
325+
)
326+
) and
211327
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
212328
)
213329
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction {
13101310
result = getAnOperand()
13111311
}
13121312

1313+
/**
1314+
* Gets the `Function` that the call targets, if this is statically known.
1315+
*/
1316+
final Function getStaticCallTarget() {
1317+
result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
1318+
}
1319+
13131320
/**
13141321
* Gets all of the arguments of the call, including the `this` pointer, if any.
13151322
*/

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction {
13101310
result = getAnOperand()
13111311
}
13121312

1313+
/**
1314+
* Gets the `Function` that the call targets, if this is statically known.
1315+
*/
1316+
final Function getStaticCallTarget() {
1317+
result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
1318+
}
1319+
13131320
/**
13141321
* Gets all of the arguments of the call, including the `this` pointer, if any.
13151322
*/

0 commit comments

Comments
 (0)