Skip to content

Commit 021aa64

Browse files
authored
Merge pull request #4142 from MathiasVP/mathiasvp/read-step-without-memory-operands
C++: Use IR alias analysis for field flow
2 parents 34a57e2 + 2d57abd commit 021aa64

File tree

30 files changed

+345
-48
lines changed

30 files changed

+345
-48
lines changed

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,23 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
144144
*/
145145
predicate jumpStep(Node n1, Node n2) { none() }
146146

147+
/**
148+
* Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
149+
*/
150+
private Field getAField(Class c, int startBit, int endBit) {
151+
result.getDeclaringType() = c and
152+
startBit = 8 * result.getByteOffset() and
153+
endBit = 8 * result.getType().getSize() + startBit
154+
or
155+
exists(Field f, Class cInner |
156+
f = c.getAField() and
157+
cInner = f.getUnderlyingType() and
158+
result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
159+
)
160+
}
161+
147162
private newtype TContent =
148-
TFieldContent(Field f) or
163+
TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
149164
TCollectionContent() or
150165
TArrayContent()
151166

@@ -163,17 +178,18 @@ class Content extends TContent {
163178
}
164179

165180
private class FieldContent extends Content, TFieldContent {
166-
Field f;
181+
Class c;
182+
int startBit;
183+
int endBit;
167184

168-
FieldContent() { this = TFieldContent(f) }
185+
FieldContent() { this = TFieldContent(c, startBit, endBit) }
169186

170-
Field getField() { result = f }
187+
// Ensure that there's just 1 result for `toString`.
188+
override string toString() { result = min(Field f | f = getAField() | f.toString()) }
171189

172-
override string toString() { result = f.toString() }
190+
predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
173191

174-
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
175-
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
176-
}
192+
Field getAField() { result = getAField(c, startBit, endBit) }
177193
}
178194

179195
private class CollectionContent extends Content, TCollectionContent {
@@ -185,20 +201,38 @@ private class ArrayContent extends Content, TArrayContent {
185201
}
186202

187203
private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) {
188-
exists(FieldAddressInstruction fa, StoreInstruction store |
204+
exists(StoreInstruction store, Class c |
189205
store = node2.asInstruction() and
190-
store.getDestinationAddress() = fa and
191206
store.getSourceValue() = node1.asInstruction() and
192-
f.(FieldContent).getField() = fa.getField()
207+
getWrittenField(store, f.(FieldContent).getAField(), c) and
208+
f.(FieldContent).hasOffset(c, _, _)
209+
)
210+
}
211+
212+
pragma[noinline]
213+
private predicate getWrittenField(StoreInstruction store, Field f, Class c) {
214+
exists(FieldAddressInstruction fa |
215+
fa = store.getDestinationAddress() and
216+
f = fa.getField() and
217+
c = f.getDeclaringType()
193218
)
194219
}
195220

196221
private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
197-
exists(FieldAddressInstruction fa, StoreInstruction store |
222+
exists(StoreInstruction store, ChiInstruction chi |
198223
node1.asInstruction() = store and
199-
store.getDestinationAddress() = fa and
200-
node2.asInstruction().(ChiInstruction).getPartial() = store and
201-
f.(FieldContent).getField() = fa.getField()
224+
node2.asInstruction() = chi and
225+
chi.getPartial() = store and
226+
exists(Class c |
227+
c = chi.getResultType() and
228+
exists(int startBit, int endBit |
229+
chi.getUpdatedInterval(startBit, endBit) and
230+
f.(FieldContent).hasOffset(c, startBit, endBit)
231+
)
232+
or
233+
getWrittenField(store, f.(FieldContent).getAField(), c) and
234+
f.(FieldContent).hasOffset(c, _, _)
235+
)
202236
)
203237
}
204238

@@ -212,17 +246,37 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
212246
storeStepChi(node1, f, node2)
213247
}
214248

249+
bindingset[result, i]
250+
private int unbindInt(int i) { i <= result and i >= result }
251+
252+
pragma[noinline]
253+
private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
254+
exists(FieldAddressInstruction fa |
255+
fa = load.getSourceAddress() and
256+
f = fa.getField() and
257+
c = f.getDeclaringType()
258+
)
259+
}
260+
215261
/**
216262
* Holds if data can flow from `node1` to `node2` via a read of `f`.
217263
* Thus, `node1` references an object with a field `f` whose value ends up in
218264
* `node2`.
219265
*/
220266
predicate readStep(Node node1, Content f, Node node2) {
221-
exists(FieldAddressInstruction fa, LoadInstruction load |
222-
load.getSourceAddress() = fa and
267+
exists(LoadInstruction load |
268+
node2.asInstruction() = load and
223269
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
224-
fa.getField() = f.(FieldContent).getField() and
225-
load = node2.asInstruction()
270+
exists(Class c |
271+
c = load.getSourceValueOperand().getAnyDef().getResultType() and
272+
exists(int startBit, int endBit |
273+
load.getSourceValueOperand().getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
274+
f.(FieldContent).hasOffset(c, startBit, endBit)
275+
)
276+
or
277+
getLoadedField(load, f.(FieldContent).getAField(), c) and
278+
f.(FieldContent).hasOffset(c, _, _)
279+
)
226280
)
227281
}
228282

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,14 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
335335

336336
private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
337337
override ChiInstruction instr;
338-
FieldAddressInstruction field;
338+
StoreInstruction store;
339339

340340
ExplicitFieldStoreQualifierNode() {
341341
not instr.isResultConflated() and
342-
exists(StoreInstruction store |
343-
instr.getPartial() = store and field = store.getDestinationAddress()
342+
instr.getPartial() = store and
343+
(
344+
instr.getUpdatedInterval(_, _) or
345+
store.getDestinationAddress() instanceof FieldAddressInstruction
344346
)
345347
}
346348

@@ -351,7 +353,12 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
351353
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
352354

353355
override Expr getDefinedExpr() {
354-
result = field.getObjectAddress().getUnconvertedResultExpression()
356+
result =
357+
store
358+
.getDestinationAddress()
359+
.(FieldAddressInstruction)
360+
.getObjectAddress()
361+
.getUnconvertedResultExpression()
355362
}
356363
}
357364

@@ -363,17 +370,22 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
363370
*/
364371
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
365372
override StoreInstruction instr;
366-
FieldAddressInstruction field;
367373

368374
ExplicitSingleFieldStoreQualifierNode() {
369-
field = instr.getDestinationAddress() and
370-
not exists(ChiInstruction chi | chi.getPartial() = instr)
375+
not exists(ChiInstruction chi | chi.getPartial() = instr) and
376+
// Without this condition any store would create a `PostUpdateNode`.
377+
instr.getDestinationAddress() instanceof FieldAddressInstruction
371378
}
372379

373380
override Node getPreUpdateNode() { none() }
374381

375382
override Expr getDefinedExpr() {
376-
result = field.getObjectAddress().getUnconvertedResultExpression()
383+
result =
384+
instr
385+
.getDestinationAddress()
386+
.(FieldAddressInstruction)
387+
.getObjectAddress()
388+
.getUnconvertedResultExpression()
377389
}
378390
}
379391

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
@@ -1962,6 +1962,13 @@ class ChiInstruction extends Instruction {
19621962
* Gets the operand that represents the new value written by the memory write.
19631963
*/
19641964
final Instruction getPartial() { result = getPartialOperand().getDef() }
1965+
1966+
/**
1967+
* Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand.
1968+
*/
1969+
final predicate getUpdatedInterval(int startBit, int endBit) {
1970+
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
1971+
}
19651972
}
19661973

19671974
/**

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,14 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper
328328
not Construction::isInCycle(useInstr) and
329329
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
330330
}
331+
332+
/**
333+
* Holds if the operand totally overlaps with its definition and consumes the
334+
* bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
335+
*/
336+
predicate getUsedInterval(int startBitOffset, int endBitOffset) {
337+
Construction::getUsedInterval(this, startBitOffset, endBitOffset)
338+
}
331339
}
332340

333341
/**

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
617617
)
618618
)
619619
}
620+
621+
/** Gets the start bit offset of a `MemoryLocation`, if any. */
622+
int getStartBitOffset(VariableMemoryLocation location) { result = location.getStartBitOffset() }
623+
624+
/** Gets the end bit offset of a `MemoryLocation`, if any. */
625+
int getEndBitOffset(VariableMemoryLocation location) { result = location.getEndBitOffset() }

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,34 @@ private module Cached {
149149
)
150150
}
151151

152+
/**
153+
* Holds if the partial operand of this `ChiInstruction` updates the bit range
154+
* `[startBitOffset, endBitOffset)` of the total operand.
155+
*/
156+
cached
157+
predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBitOffset, int endBitOffset) {
158+
exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
159+
oldInstruction = getOldInstruction(chi.getPartial()) and
160+
location = Alias::getResultMemoryLocation(oldInstruction) and
161+
startBitOffset = Alias::getStartBitOffset(location) and
162+
endBitOffset = Alias::getEndBitOffset(location)
163+
)
164+
}
165+
166+
/**
167+
* Holds if `operand` totally overlaps with its definition and consumes the bit range
168+
* `[startBitOffset, endBitOffset)`.
169+
*/
170+
cached
171+
predicate getUsedInterval(NonPhiMemoryOperand operand, int startBitOffset, int endBitOffset) {
172+
exists(Alias::MemoryLocation location, OldIR::NonPhiMemoryOperand oldOperand |
173+
oldOperand = operand.getUse().(OldInstruction).getAnOperand() and
174+
location = Alias::getOperandMemoryLocation(oldOperand) and
175+
startBitOffset = Alias::getStartBitOffset(location) and
176+
endBitOffset = Alias::getEndBitOffset(location)
177+
)
178+
}
179+
152180
/**
153181
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
154182
* through a phi instruction and therefore should be impossible.

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
@@ -1962,6 +1962,13 @@ class ChiInstruction extends Instruction {
19621962
* Gets the operand that represents the new value written by the memory write.
19631963
*/
19641964
final Instruction getPartial() { result = getPartialOperand().getDef() }
1965+
1966+
/**
1967+
* Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand.
1968+
*/
1969+
final predicate getUpdatedInterval(int startBit, int endBit) {
1970+
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
1971+
}
19651972
}
19661973

19671974
/**

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,14 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper
328328
not Construction::isInCycle(useInstr) and
329329
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
330330
}
331+
332+
/**
333+
* Holds if the operand totally overlaps with its definition and consumes the
334+
* bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
335+
*/
336+
predicate getUsedInterval(int startBitOffset, int endBitOffset) {
337+
Construction::getUsedInterval(this, startBitOffset, endBitOffset)
338+
}
331339
}
332340

333341
/**

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,18 @@ Instruction getMemoryOperandDefinition(
182182
none()
183183
}
184184

185+
/**
186+
* Holds if the partial operand of this `ChiInstruction` updates the bit range
187+
* `[startBitOffset, endBitOffset)` of the total operand.
188+
*/
189+
predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBit, int endBit) { none() }
190+
191+
/**
192+
* Holds if the operand totally overlaps with its definition and consumes the
193+
* bit range `[startBitOffset, endBitOffset)`.
194+
*/
195+
predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() }
196+
185197
/** Gets a non-phi instruction that defines an operand of `instr`. */
186198
private Instruction getNonPhiOperandDef(Instruction instr) {
187199
result = getRegisterOperandDefinition(instr, _)

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
@@ -1962,6 +1962,13 @@ class ChiInstruction extends Instruction {
19621962
* Gets the operand that represents the new value written by the memory write.
19631963
*/
19641964
final Instruction getPartial() { result = getPartialOperand().getDef() }
1965+
1966+
/**
1967+
* Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand.
1968+
*/
1969+
final predicate getUpdatedInterval(int startBit, int endBit) {
1970+
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
1971+
}
19651972
}
19661973

19671974
/**

0 commit comments

Comments
 (0)