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

Skip to content

Commit b6b17fe

Browse files
committed
C++: Add a read and store step that replace ArrayContent with FieldContent when we realize that the target of a store is a field.
1 parent dff9f82 commit b6b17fe

2 files changed

Lines changed: 107 additions & 7 deletions

File tree

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

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,17 @@ private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode
211211
)
212212
}
213213

214+
private FieldAddressInstruction getFieldInstruction(Instruction instr) {
215+
result = instr or
216+
result = instr.(CopyValueInstruction).getUnary()
217+
}
218+
214219
pragma[noinline]
215-
private predicate getWrittenField(StoreInstruction store, Field f, Class c) {
220+
private predicate getWrittenField(Instruction instr, Field f, Class c) {
216221
exists(FieldAddressInstruction fa |
217-
fa = store.getDestinationAddress() and
222+
fa =
223+
getFieldInstruction([any(StoreInstruction store | instr = store).getDestinationAddress(),
224+
any(WriteSideEffectInstruction write | instr = write).getDestinationAddress()]) and
218225
f = fa.getField() and
219226
c = f.getDeclaringType()
220227
)
@@ -265,7 +272,23 @@ private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode n
265272
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
266273
fieldStoreStepNoChi(node1, f, node2) or
267274
fieldStoreStepChi(node1, f, node2) or
268-
arrayStoreStepChi(node1, f, node2)
275+
arrayStoreStepChi(node1, f, node2) or
276+
fieldStoreStepAfterArraySuppression(node1, f, node2)
277+
}
278+
279+
// This predicate pushes the correct `FieldContent` onto the access path when the
280+
// `suppressArrayRead` predicate has popped off an `ArrayContent`.
281+
private predicate fieldStoreStepAfterArraySuppression(
282+
Node node1, FieldContent f, PostUpdateNode node2
283+
) {
284+
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi, Class c |
285+
not chi.isResultConflated() and
286+
node1.asInstruction() = chi and
287+
node2.asInstruction() = chi and
288+
chi.getPartial() = write and
289+
getWrittenField(write, f.getAField(), c) and
290+
f.hasOffset(c, _, _)
291+
)
269292
}
270293

271294
bindingset[result, i]
@@ -302,11 +325,53 @@ private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
302325
)
303326
}
304327

328+
predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
329+
a = TArrayContent() and
330+
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
331+
node1.asInstruction() = write and
332+
node2.asInstruction() = chi and
333+
chi.getPartial() = write and
334+
getWrittenField(write, _, _)
335+
)
336+
}
337+
338+
private class ArrayToPointerConvertInstruction extends ConvertInstruction {
339+
ArrayToPointerConvertInstruction() {
340+
this.getUnary().getResultType() instanceof ArrayType and
341+
this.getResultType() instanceof PointerType
342+
}
343+
}
344+
305345
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
306346
a = TArrayContent() and
307-
exists(LoadInstruction load |
308-
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
309-
load = node2.asInstruction()
347+
(
348+
// In cases such as:
349+
// ```cpp
350+
// void f(int* pa) {
351+
// *pa = source();
352+
// }
353+
// ...
354+
// int x;
355+
// f(&x);
356+
// use(x);
357+
// ```
358+
// the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
359+
// is a `BufferMayWriteSideEffect`).
360+
exists(LoadInstruction load |
361+
node1.asInstruction() = load.getSourceValue() and
362+
load = node2.asInstruction()
363+
)
364+
or
365+
// Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
366+
exists(LoadInstruction load |
367+
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
368+
load = node2.asInstruction() and
369+
(
370+
load.getSourceAddress() instanceof LoadInstruction or
371+
load.getSourceAddress() instanceof ArrayToPointerConvertInstruction or
372+
load.getSourceAddress() instanceof PointerAddInstruction
373+
)
374+
)
310375
)
311376
}
312377

@@ -317,7 +382,19 @@ private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
317382
*/
318383
predicate readStep(Node node1, Content f, Node node2) {
319384
fieldReadStep(node1, f, node2) or
320-
arrayReadStep(node1, f, node2)
385+
arrayReadStep(node1, f, node2) or
386+
// When a store step happens in a function that looks like an array write such as:
387+
// ```cpp
388+
// void f(int* pa) {
389+
// *pa = source();
390+
// }
391+
// ```
392+
// it can be a write to an array, but it can also happen that `f` is called as `f(&a.x)`. If that is
393+
// the case, the `ArrayContent` that was written by the call to `f` should be popped off the access
394+
// path, and a `FieldContent` containing `x` should be pushed instead.
395+
// So this case pops `ArrayContent` off the access path, and the `fieldStoreStepAfterArraySuppression`
396+
// predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path.
397+
suppressArrayRead(node1, f, node2)
321398
}
322399

323400
/**

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,29 @@ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNod
389389
}
390390
}
391391

392+
private FieldAddressInstruction getFieldInstruction(Instruction instr) {
393+
result = instr or
394+
result = instr.(CopyValueInstruction).getUnary()
395+
}
396+
397+
private class BufferMayWriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
398+
override ChiInstruction instr;
399+
BufferMayWriteSideEffectInstruction write;
400+
FieldAddressInstruction field;
401+
402+
BufferMayWriteSideEffectFieldStoreQualifierNode() {
403+
not instr.isResultConflated() and
404+
instr.getPartial() = write and
405+
field = getFieldInstruction(write.getDestinationAddress())
406+
}
407+
408+
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
409+
410+
override Expr getDefinedExpr() {
411+
result = field.getObjectAddress().getUnconvertedResultExpression()
412+
}
413+
}
414+
392415
/**
393416
* The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
394417
* `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.

0 commit comments

Comments
 (0)