Skip to content

Commit b1d4ca5

Browse files
authored
Merge pull request github#14599 from aschackmull/dataflow/partialflow-separate
Dataflow: Restrict partial flow to either forward or reverse flow.
2 parents 8198898 + bbc3cfb commit b1d4ca5

File tree

2 files changed

+110
-84
lines changed

2 files changed

+110
-84
lines changed

docs/codeql/writing-codeql-queries/debugging-data-flow-queries-using-partial-flow.rst

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Partial flow
5959

6060
A naive next step could be to change the sink definition to ``any()``. This would mean that we would get a lot of flow to all the places that are reachable from the sources. While this approach may work in some cases, you might find that it produces so many results that it's very hard to explore the findings. It can also dramatically affect query performance. More importantly, you might not even see all the partial flow paths. This is because the data-flow library tries very hard to prune impossible paths and, since field stores and reads must be evenly matched along a path, we will never see paths going through a store that fail to reach a corresponding read. This can make it hard to see where flow actually stops.
6161

62-
To avoid these problems, the data-flow library comes with a mechanism for exploring partial flow that tries to deal with these caveats. This is the ``MyFlow::FlowExploration<explorationLimit/0>::partialFlow`` predicate:
62+
To avoid these problems, the data-flow library comes with a mechanism for exploring partial flow that tries to deal with these caveats. This is the ``MyFlow::FlowExplorationFwd<explorationLimit/0>::partialFlow`` predicate:
6363

6464
.. code-block:: ql
6565
@@ -77,21 +77,19 @@ To avoid these problems, the data-flow library comes with a mechanism for explor
7777
*/
7878
predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) {
7979
80-
There is also a ``partialFlowRev`` for exploring flow backwards from a sink.
80+
There is also a ``MyFlow::FlowExplorationRev<explorationLimit/0>::partialFlow`` for exploring flow backwards from a sink.
8181

82-
To get access to these predicates you must instantiate the ``MyFlow::FlowExploration<>`` module with an exploration limit. For example:
82+
To get access to these predicates you must instantiate the ``MyFlow::FlowExplorationFwd<>`` module with an exploration limit (or the ``MyFlow::FlowExplorationRev<>`` module for reverse flow). For example:
8383

8484
.. code-block:: ql
8585
8686
int explorationLimit() { result = 5 }
8787
88-
module MyPartialFlow = MyFlow::FlowExploration<explorationLimit/0>;
88+
module MyPartialFlow = MyFlow::FlowExplorationFwd<explorationLimit/0>;
8989
9090
This defines the exploration radius within which ``partialFlow`` returns results.
9191

92-
To get good performance when using ``partialFlow`` it is important to ensure the ``isSink`` predicate of the configuration has no results. Likewise, when using ``partialFlowRev`` the ``isSource`` predicate of the configuration should have no results.
93-
94-
It is also useful to focus on a single source at a time as the starting point for the flow exploration. This is most easily done by adding a temporary restriction in the ``isSource`` predicate.
92+
It is useful to focus on a single source at a time as the starting point for the flow exploration. This is most easily done by adding a temporary restriction in the ``isSource`` predicate.
9593

9694
To do quick evaluations of partial flow it is often easiest to add a predicate to the query that is solely intended for quick evaluation (right-click the predicate name and choose "CodeQL: Quick Evaluation"). A good starting point is something like:
9795

shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll

Lines changed: 105 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4357,7 +4357,29 @@ module MakeImpl<InputSig Lang> {
43574357
tftuples = -1
43584358
}
43594359

4360-
module FlowExploration<explorationLimitSig/0 explorationLimit> {
4360+
private signature predicate flag();
4361+
4362+
private predicate flagEnable() { any() }
4363+
4364+
private predicate flagDisable() { none() }
4365+
4366+
module FlowExplorationFwd<explorationLimitSig/0 explorationLimit> {
4367+
private import FlowExploration<explorationLimit/0, flagEnable/0, flagDisable/0>
4368+
import Public
4369+
4370+
predicate partialFlow = partialFlowFwd/3;
4371+
}
4372+
4373+
module FlowExplorationRev<explorationLimitSig/0 explorationLimit> {
4374+
private import FlowExploration<explorationLimit/0, flagDisable/0, flagEnable/0>
4375+
import Public
4376+
4377+
predicate partialFlow = partialFlowRev/3;
4378+
}
4379+
4380+
private module FlowExploration<
4381+
explorationLimitSig/0 explorationLimit, flag/0 flagFwd, flag/0 flagRev>
4382+
{
43614383
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) {
43624384
exists(NodeEx node1, NodeEx node2 |
43634385
jumpStepEx(node1, node2)
@@ -4526,6 +4548,7 @@ module MakeImpl<InputSig Lang> {
45264548
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
45274549
TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
45284550
) {
4551+
flagFwd() and
45294552
sourceNode(node, state) and
45304553
cc instanceof CallContextAny and
45314554
sc1 = TSummaryCtx1None() and
@@ -4543,6 +4566,7 @@ module MakeImpl<InputSig Lang> {
45434566
NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
45444567
TRevSummaryCtx3 sc3, PartialAccessPath ap
45454568
) {
4569+
flagRev() and
45464570
revSinkNode(node, state) and
45474571
sc1 = TRevSummaryCtx1None() and
45484572
sc2 = TRevSummaryCtx2None() and
@@ -4595,96 +4619,100 @@ module MakeImpl<InputSig Lang> {
45954619
partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t
45964620
}
45974621

4598-
/**
4599-
* A `Node` augmented with a call context, an access path, and a configuration.
4600-
*/
4601-
class PartialPathNode extends TPartialPathNode {
4602-
/** Gets a textual representation of this element. */
4603-
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
4604-
4622+
module Public {
46054623
/**
4606-
* Gets a textual representation of this element, including a textual
4607-
* representation of the call context.
4624+
* A `Node` augmented with a call context, an access path, and a configuration.
46084625
*/
4609-
string toStringWithContext() {
4610-
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
4611-
}
4626+
class PartialPathNode extends TPartialPathNode {
4627+
/** Gets a textual representation of this element. */
4628+
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
4629+
4630+
/**
4631+
* Gets a textual representation of this element, including a textual
4632+
* representation of the call context.
4633+
*/
4634+
string toStringWithContext() {
4635+
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
4636+
}
46124637

4613-
/**
4614-
* Holds if this element is at the specified location.
4615-
* The location spans column `startcolumn` of line `startline` to
4616-
* column `endcolumn` of line `endline` in file `filepath`.
4617-
* For more information, see
4618-
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
4619-
*/
4620-
predicate hasLocationInfo(
4621-
string filepath, int startline, int startcolumn, int endline, int endcolumn
4622-
) {
4623-
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
4624-
}
4638+
/**
4639+
* Holds if this element is at the specified location.
4640+
* The location spans column `startcolumn` of line `startline` to
4641+
* column `endcolumn` of line `endline` in file `filepath`.
4642+
* For more information, see
4643+
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
4644+
*/
4645+
predicate hasLocationInfo(
4646+
string filepath, int startline, int startcolumn, int endline, int endcolumn
4647+
) {
4648+
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
4649+
}
46254650

4626-
/** Gets the underlying `Node`. */
4627-
final Node getNode() { this.getNodeEx().projectToNode() = result }
4651+
/** Gets the underlying `Node`. */
4652+
final Node getNode() { this.getNodeEx().projectToNode() = result }
46284653

4629-
FlowState getState() { none() }
4654+
FlowState getState() { none() }
46304655

4631-
private NodeEx getNodeEx() {
4632-
result = this.(PartialPathNodeFwd).getNodeEx() or
4633-
result = this.(PartialPathNodeRev).getNodeEx()
4634-
}
4656+
private NodeEx getNodeEx() {
4657+
result = this.(PartialPathNodeFwd).getNodeEx() or
4658+
result = this.(PartialPathNodeRev).getNodeEx()
4659+
}
46354660

4636-
/** Gets a successor of this node, if any. */
4637-
PartialPathNode getASuccessor() { none() }
4661+
/** Gets a successor of this node, if any. */
4662+
PartialPathNode getASuccessor() { none() }
46384663

4639-
/**
4640-
* Gets the approximate distance to the nearest source measured in number
4641-
* of interprocedural steps.
4642-
*/
4643-
int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) }
4664+
/**
4665+
* Gets the approximate distance to the nearest source measured in number
4666+
* of interprocedural steps.
4667+
*/
4668+
int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) }
46444669

4645-
/**
4646-
* Gets the approximate distance to the nearest sink measured in number
4647-
* of interprocedural steps.
4648-
*/
4649-
int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) }
4670+
/**
4671+
* Gets the approximate distance to the nearest sink measured in number
4672+
* of interprocedural steps.
4673+
*/
4674+
int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) }
46504675

4651-
private string ppType() {
4652-
this instanceof PartialPathNodeRev and result = ""
4653-
or
4654-
exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() |
4655-
// The `concat` becomes "" if `ppReprType` has no result.
4656-
result = concat(" : " + ppReprType(t))
4657-
)
4658-
}
4676+
private string ppType() {
4677+
this instanceof PartialPathNodeRev and result = ""
4678+
or
4679+
exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() |
4680+
// The `concat` becomes "" if `ppReprType` has no result.
4681+
result = concat(" : " + ppReprType(t))
4682+
)
4683+
}
46594684

4660-
private string ppAp() {
4661-
exists(string s |
4662-
s = this.(PartialPathNodeFwd).getAp().toString() or
4663-
s = this.(PartialPathNodeRev).getAp().toString()
4664-
|
4665-
if s = "" then result = "" else result = " " + s
4666-
)
4667-
}
4685+
private string ppAp() {
4686+
exists(string s |
4687+
s = this.(PartialPathNodeFwd).getAp().toString() or
4688+
s = this.(PartialPathNodeRev).getAp().toString()
4689+
|
4690+
if s = "" then result = "" else result = " " + s
4691+
)
4692+
}
46684693

4669-
private string ppCtx() {
4670-
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
4671-
}
4694+
private string ppCtx() {
4695+
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
4696+
}
46724697

4673-
/** Holds if this is a source in a forward-flow path. */
4674-
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
4698+
/** Holds if this is a source in a forward-flow path. */
4699+
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
46754700

4676-
/** Holds if this is a sink in a reverse-flow path. */
4677-
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
4678-
}
4701+
/** Holds if this is a sink in a reverse-flow path. */
4702+
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
4703+
}
46794704

4680-
/**
4681-
* Provides the query predicates needed to include a graph in a path-problem query.
4682-
*/
4683-
module PartialPathGraph {
4684-
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
4685-
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
4705+
/**
4706+
* Provides the query predicates needed to include a graph in a path-problem query.
4707+
*/
4708+
module PartialPathGraph {
4709+
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
4710+
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
4711+
}
46864712
}
46874713

4714+
import Public
4715+
46884716
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
46894717
NodeEx node;
46904718
FlowState state;
@@ -5223,7 +5251,7 @@ module MakeImpl<InputSig Lang> {
52235251
)
52245252
}
52255253

5226-
private predicate partialFlow(PartialPathNode source, PartialPathNode node) {
5254+
private predicate fwdPartialFlow(PartialPathNode source, PartialPathNode node) {
52275255
source.isFwdSource() and
52285256
node = source.getASuccessor+()
52295257
}
@@ -5245,8 +5273,8 @@ module MakeImpl<InputSig Lang> {
52455273
*
52465274
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
52475275
*/
5248-
predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) {
5249-
partialFlow(source, node) and
5276+
predicate partialFlowFwd(PartialPathNode source, PartialPathNode node, int dist) {
5277+
fwdPartialFlow(source, node) and
52505278
dist = node.getSourceDistance()
52515279
}
52525280

0 commit comments

Comments
 (0)