Skip to content

Commit 032fcdc

Browse files
committed
C++: Don't rely on reverse reads to obtain nested store steps, as this
creates bad performance. In the AST dataflow library the 'final' reverse read step will read from the beginning of a chain of field dereferences (i.e., the x in x.y.z = source()). Typically, this start node will not be a source in the localFlowStep relation. This is different in the IR, where the 'final' reverse read step (up until this commit) was the total chi operand, and this total chi operand will typically be the source of several simpleLocalFlowStep results. This can create pathological cases where a node can both end a big step in the dataflow library, but also be part of a _bigger_ big step. This caused a huge performance regression on openjdk. This commit avoids reverse reads, and instead creates explicit store steps for nested field dereferences. We compute the final step from the FieldNode to the total chi operand as a simpleFlowStep instead, thus avoiding the performance problem. Lesson learned: Be sure that the target of your readStep isn't also a source in your localFlowStep relation.
1 parent d07f393 commit 032fcdc

1 file changed

Lines changed: 18 additions & 17 deletions

File tree

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

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode n
284284
)
285285
}
286286

287+
/**
288+
* A store step from one `FieldNode` that is used in a `storeStep` to another `FieldNode`.
289+
* For instance in `a.b.c = source();`, where they will be a `nestedFieldStore` from
290+
* the `FieldNode` of `c` to the `FieldNode` of `b`.
291+
*/
292+
predicate nestedFieldStore(PostUpdateNode node1, FieldContent f, FieldNode node2) {
293+
node2 = node1.getPreUpdateNode() and
294+
f.getADirectField() = node2.getField()
295+
}
296+
287297
/**
288298
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
289299
* Thus, `node2` references an object with a field `f` that contains the
@@ -293,7 +303,8 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
293303
instrToFieldNodeStoreStepNoChi(node1, f, node2) or
294304
instrToFieldNodeStoreStepChi(node1, f, node2) or
295305
arrayStoreStepChi(node1, f, node2) or
296-
fieldStoreStepAfterArraySuppression(node1, f, node2)
306+
fieldStoreStepAfterArraySuppression(node1, f, node2) or
307+
nestedFieldStore(node1, f, node2)
297308
}
298309

299310
/**
@@ -396,22 +407,12 @@ private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
396407
}
397408

398409
/**
399-
* Given two nodes `node1` and `node2`, the shared dataflow library infers a store step
400-
* from `node1` to `node2` if there is a read step from the the pre update node of `node2` to the
401-
* the pre update node of `node1`. This ensures that a field assignment such as
402-
* ```cpp
403-
* x.y.z = source();
404-
* ```
405-
* generates the access path `[z, y, x]`. This predicate ensures that there's a read step from
406-
* `x` (the pre update node of `y`) to `y` (the pre update node of `z`), which means that the
407-
* shared dataflow library will infer a store step from `z` to `y`.
410+
* A read step that reads from a nested `FieldNode` in cases such as
411+
* `x = a.b.c;`. In this case, there is a `nestedFieldReap` read step from the `FieldNode` of `b`
412+
* to the `FieldNode` of `c`.
408413
*/
409-
private predicate reverseReadStep(Node node1, FieldContent f, FieldNode node2) {
410-
(
411-
node1 = node2.getObjectNode()
412-
or
413-
not exists(node2.getObjectNode()) and node1 = node2.getRootNode()
414-
) and
414+
private predicate nestedFieldRead(Node node1, FieldContent f, FieldNode node2) {
415+
node1 = node2.getObjectNode() and
415416
f.getADirectField() = node2.getField()
416417
}
417418

@@ -455,7 +456,7 @@ predicate readStep(Node node1, Content f, Node node2) {
455456
arrayReadStep(node1, f, node2) or
456457
exactReadStep(node1, f, node2) or
457458
suppressArrayRead(node1, f, node2) or
458-
reverseReadStep(node1, f, node2) or
459+
nestedFieldRead(node1, f, node2) or
459460
instrToFieldNodeReadStep(node1, f, node2)
460461
}
461462

0 commit comments

Comments
 (0)