Skip to content

Commit b2a87f6

Browse files
authored
Merge pull request #2696 from dbartol/dbartol/Indirections
C++/C#: Alias analysis of indirect parameters
2 parents dd517a4 + 148e87c commit b2a87f6

40 files changed

+1518
-828
lines changed

config/identical-files.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,14 @@
190190
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
191191
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
192192
],
193-
"C++ SSA AliasAnalysis": [
193+
"SSA AliasAnalysis": [
194194
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
195-
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
195+
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
196+
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
197+
],
198+
"C++ SSA AliasAnalysisImports": [
199+
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
200+
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
196201
],
197202
"C++ IR ValueNumberingImports": [
198203
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
@@ -203,6 +208,10 @@
203208
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
204209
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
205210
],
211+
"IR AliasConfiguration (unaliased_ssa)": [
212+
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
213+
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
214+
],
206215
"IR SSA SSAConstruction": [
207216
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
208217
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",

cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private newtype TMemoryAccessKind =
22
TIndirectMemoryAccess() or
33
TBufferMemoryAccess() or
4+
TEntireAllocationMemoryAccess() or
45
TEscapedMemoryAccess() or
56
TNonLocalMemoryAccess() or
67
TPhiMemoryAccess() or
@@ -43,6 +44,16 @@ class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
4344
final override predicate usesAddressOperand() { any() }
4445
}
4546

47+
/**
48+
* The operand or results accesses all memory in the contiguous allocation that contains the address
49+
* specified by the `AddressOperand` on the same instruction.
50+
*/
51+
class EntireAllocationMemoryAccess extends MemoryAccessKind, TEntireAllocationMemoryAccess {
52+
override string toString() { result = "alloc" }
53+
54+
final override predicate usesAddressOperand() { any() }
55+
}
56+
4657
/**
4758
* The operand or result accesses all memory whose address has escaped.
4859
*/

cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,31 @@ abstract class BufferReadOpcode extends BufferAccessOpcode {
232232
final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess }
233233
}
234234

235+
/**
236+
* An opcode that access an entire memory allocation.
237+
*/
238+
abstract class EntireAllocationAccessOpcode extends Opcode {
239+
final override predicate hasAddressOperand() { any() }
240+
}
241+
242+
/**
243+
* An opcode that write to an entire memory allocation.
244+
*/
245+
abstract class EntireAllocationWriteOpcode extends EntireAllocationAccessOpcode {
246+
final override MemoryAccessKind getWriteMemoryAccess() {
247+
result instanceof EntireAllocationMemoryAccess
248+
}
249+
}
250+
251+
/**
252+
* An opcode that reads from an entire memory allocation.
253+
*/
254+
abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode {
255+
final override MemoryAccessKind getReadMemoryAccess() {
256+
result instanceof EntireAllocationMemoryAccess
257+
}
258+
}
259+
235260
/**
236261
* An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`.
237262
*/
@@ -325,7 +350,7 @@ module Opcode {
325350
final override string toString() { result = "InitializeParameter" }
326351
}
327352

328-
class InitializeIndirection extends IndirectWriteOpcode, TInitializeIndirection {
353+
class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection {
329354
final override string toString() { result = "InitializeIndirection" }
330355
}
331356

@@ -349,7 +374,7 @@ module Opcode {
349374
final override string toString() { result = "ReturnVoid" }
350375
}
351376

352-
class ReturnIndirection extends IndirectReadOpcode, TReturnIndirection {
377+
class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection {
353378
final override string toString() { result = "ReturnIndirection" }
354379

355380
final override predicate hasOperandInternal(OperandTag tag) {

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

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
private import AliasAnalysisInternal
2-
private import cpp
32
private import InputIR
4-
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
5-
private import semmle.code.cpp.ir.implementation.IRConfiguration
6-
private import semmle.code.cpp.models.interfaces.Alias
3+
private import AliasAnalysisImports
74

85
private class IntValue = Ints::IntValue;
96

10-
/**
11-
* Gets the offset of field `field` in bits.
12-
*/
13-
private IntValue getFieldBitOffset(Field field) {
14-
if field instanceof BitField
15-
then result = Ints::add(Ints::mul(field.getByteOffset(), 8), field.(BitField).getBitOffset())
16-
else result = Ints::mul(field.getByteOffset(), 8)
17-
}
18-
197
/**
208
* Holds if the operand `tag` of instruction `instr` is used in a way that does
219
* not result in any address held in that operand from escaping beyond the
@@ -36,7 +24,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
3624
instr instanceof PointerDiffInstruction
3725
or
3826
// Converting an address to a `bool` does not escape the address.
39-
instr.(ConvertInstruction).getResultType() instanceof BoolType
27+
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
4028
)
4129
)
4230
or
@@ -111,13 +99,10 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
11199
bitOffset = Ints::unknown()
112100
or
113101
// Conversion to another pointer type propagates the source address.
114-
exists(ConvertInstruction convert, Type resultType |
102+
exists(ConvertInstruction convert, IRType resultType |
115103
convert = instr and
116-
resultType = convert.getResultType() and
117-
(
118-
resultType instanceof PointerType or
119-
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
120-
) and
104+
resultType = convert.getResultIRType() and
105+
resultType instanceof IRAddressType and
121106
bitOffset = 0
122107
)
123108
or
@@ -131,7 +116,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
131116
or
132117
// Computing a field address from a pointer propagates the address plus the
133118
// offset of the field.
134-
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField())
119+
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
135120
or
136121
// A copy propagates the source value.
137122
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
@@ -212,7 +197,7 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
212197
}
213198

214199
private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
215-
exists(Function f |
200+
exists(Language::Function f |
216201
ci = operand.getUse() and
217202
f = ci.getStaticCallTarget() and
218203
(
@@ -223,27 +208,27 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
223208
init.getEnclosingFunction() = f and
224209
operand instanceof ThisArgumentOperand
225210
) and
226-
not f.isVirtual() and
227-
not f instanceof AliasFunction
211+
not Language::isFunctionVirtual(f) and
212+
not f instanceof AliasModels::AliasFunction
228213
)
229214
}
230215

231216
private predicate isAlwaysReturnedArgument(Operand operand) {
232-
exists(AliasFunction f |
217+
exists(AliasModels::AliasFunction f |
233218
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
234219
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
235220
)
236221
}
237222

238223
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
239-
exists(AliasFunction f |
224+
exists(AliasModels::AliasFunction f |
240225
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
241226
f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
242227
)
243228
}
244229

245230
private predicate isNeverEscapesArgument(Operand operand) {
246-
exists(AliasFunction f |
231+
exists(AliasModels::AliasFunction f |
247232
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
248233
f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
249234
)
@@ -265,61 +250,86 @@ private predicate resultEscapesNonReturn(Instruction instr) {
265250
}
266251

267252
/**
268-
* Holds if the address of the specified local variable or parameter escapes the
269-
* domain of the analysis.
253+
* Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur
254+
* either because the allocation's address is taken within the function and escapes, or because the
255+
* allocation is marked as always escaping via `alwaysEscapes()`.
270256
*/
271-
private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
272-
// The variable's address escapes if the result of any
273-
// VariableAddressInstruction that computes the variable's address escapes.
274-
exists(VariableAddressInstruction instr |
275-
instr.getIRVariable() = var and
276-
resultEscapesNonReturn(instr)
257+
predicate allocationEscapes(Configuration::Allocation allocation) {
258+
allocation.alwaysEscapes()
259+
or
260+
exists(IREscapeAnalysisConfiguration config |
261+
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
277262
)
278263
}
279264

280265
/**
281-
* Holds if the address of the specified variable escapes the domain of the
282-
* analysis.
266+
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
283267
*/
284-
predicate variableAddressEscapes(IRVariable var) {
285-
exists(IREscapeAnalysisConfiguration config |
286-
config.useSoundEscapeAnalysis() and
287-
automaticVariableAddressEscapes(var.(IRAutomaticVariable))
288-
)
268+
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
269+
operandIsPropagated(operand, bitOffset)
289270
or
290-
// All variables with static storage duration have their address escape, even when escape analysis
291-
// is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global
292-
// variable. Normally, we rely on `AliasedDefinition` to handle that.
293-
not var instanceof IRAutomaticVariable
271+
exists(CallInstruction call, Instruction init |
272+
isArgumentForParameter(call, operand, init) and
273+
resultReturned(init, bitOffset)
274+
)
294275
}
295276

296277
/**
297-
* Holds if the result of instruction `instr` points within variable `var`, at
298-
* bit offset `bitOffset` within the variable. If the result points within
299-
* `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown.
278+
* Holds if `addrOperand` is at offset `bitOffset` from the value of instruction `base`. The offset
279+
* may be `unknown()`.
300280
*/
301-
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
302-
// The address of a variable points to that variable, at offset 0.
303-
instr.(VariableAddressInstruction).getIRVariable() = var and
304-
bitOffset = 0
305-
or
306-
// A string literal is just a special read-only global variable.
307-
instr.(StringConstantInstruction).getIRVariable() = var and
308-
bitOffset = 0
281+
private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, IntValue bitOffset) {
282+
base = addrOperand.getDef() and bitOffset = 0 // Base case
309283
or
310-
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
311-
operand = instr.getAnOperand() and
312-
// If an operand is propagated, then the result points to the same variable,
313-
// offset by the bit offset from the propagation.
314-
resultPointsTo(operand.getAnyDef(), var, originalBitOffset) and
315-
(
316-
operandIsPropagated(operand, propagatedBitOffset)
317-
or
318-
exists(CallInstruction ci, Instruction init |
319-
isArgumentForParameter(ci, operand, init) and
320-
resultReturned(init, propagatedBitOffset)
321-
)
322-
) and
323-
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
284+
exists(
285+
Instruction middle, int previousBitOffset, Operand middleOperand, IntValue additionalBitOffset
286+
|
287+
// We already have an offset from `middle`.
288+
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
289+
// `middle` is propagated from `base`.
290+
middleOperand = middle.getAnOperand() and
291+
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
292+
base = middleOperand.getDef() and
293+
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
294+
)
295+
}
296+
297+
/**
298+
* Holds if `addrOperand` is at constant offset `bitOffset` from the value of instruction `base`.
299+
* Only holds for the `base` with the longest chain of propagation to `addrOperand`.
300+
*/
301+
predicate addressOperandBaseAndConstantOffset(
302+
AddressOperand addrOperand, Instruction base, int bitOffset
303+
) {
304+
hasBaseAndOffset(addrOperand, base, bitOffset) and
305+
Ints::hasValue(bitOffset) and
306+
not exists(Instruction previousBase, int previousBitOffset |
307+
hasBaseAndOffset(addrOperand, previousBase, previousBitOffset) and
308+
previousBase = base.getAnOperand().getDef() and
309+
Ints::hasValue(previousBitOffset)
310+
)
311+
}
312+
313+
/**
314+
* Gets the allocation into which `addrOperand` points, if known.
315+
*/
316+
Configuration::Allocation getAddressOperandAllocation(AddressOperand addrOperand) {
317+
addressOperandAllocationAndOffset(addrOperand, result, _)
318+
}
319+
320+
/**
321+
* Holds if `addrOperand` is at offset `bitOffset` from a base instruction of `allocation`. The
322+
* offset may be `unknown()`.
323+
*/
324+
predicate addressOperandAllocationAndOffset(
325+
AddressOperand addrOperand, Configuration::Allocation allocation, IntValue bitOffset
326+
) {
327+
exists(Instruction base |
328+
allocation.getABaseInstruction() = base and
329+
hasBaseAndOffset(addrOperand, base, bitOffset) and
330+
not exists(Instruction previousBase |
331+
hasBaseAndOffset(addrOperand, previousBase, _) and
332+
previousBase = base.getAnOperand().getDef()
333+
)
324334
)
325335
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import semmle.code.cpp.ir.implementation.IRConfiguration
2+
import semmle.code.cpp.ir.internal.IntegerConstant as Ints
3+
import semmle.code.cpp.models.interfaces.Alias as AliasModels
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
12
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR
3+
import AliasConfiguration as Configuration

0 commit comments

Comments
 (0)