Skip to content

Commit 11c85f0

Browse files
committed
Python: Clean up various jump/local data flow steps
Removes steps from `ModuleVariableNode`s from `essaFlowStep`, and instead puts them only in `jumpStep`. This cleans up the logic a bit. This slightly broke the type tracker implementation (as it relied on `essaFlowStep` being fairly liberal), so I have rewritten it to explicitly rely on just familiar predicates for local and jump steps. Additionally, we disallow Essa-to-Essa steps where exactly one of the two nodes corresponds to a global variable (i.e. only local-local and global-global steps).
1 parent f93c44a commit 11c85f0

File tree

3 files changed

+36
-20
lines changed

3 files changed

+36
-20
lines changed

python/ql/src/experimental/dataflow/TypeTracker.qll

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ class StepSummary extends TStepSummary {
4747
module StepSummary {
4848
cached
4949
predicate step(Node nodeFrom, Node nodeTo, StepSummary summary) {
50-
exists(Node mid | EssaFlow::essaFlowStep*(nodeFrom, mid) and smallstep(mid, nodeTo, summary))
50+
exists(Node mid | typePreservingStep*(nodeFrom, mid) and smallstep(mid, nodeTo, summary))
5151
}
5252

5353
predicate smallstep(Node nodeFrom, Node nodeTo, StepSummary summary) {
54-
EssaFlow::essaFlowStep(nodeFrom, nodeTo) and
54+
typePreservingStep(nodeFrom, nodeTo) and
5555
summary = LevelStep()
5656
or
5757
callStep(nodeFrom, nodeTo) and summary = CallStep()
@@ -68,6 +68,12 @@ module StepSummary {
6868
}
6969
}
7070

71+
/** Holds if it's reasonable to expect the data flow step from `nodeFrom` to `nodeTo` to preserve types. */
72+
private predicate typePreservingStep(Node nodeFrom, Node nodeTo) {
73+
EssaFlow::essaFlowStep(nodeFrom, nodeTo) or
74+
jumpStep(nodeFrom, nodeTo)
75+
}
76+
7177
/** Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. */
7278
predicate callStep(ArgumentNode nodeFrom, ParameterNode nodeTo) {
7379
// TODO: Support special methods?
@@ -111,7 +117,7 @@ predicate returnStep(ReturnNode nodeFrom, Node nodeTo) {
111117
predicate basicStoreStep(Node nodeFrom, Node nodeTo, string attr) {
112118
exists(AttributeAssignment a, Node var |
113119
a.getName() = attr and
114-
EssaFlow::essaFlowStep*(nodeTo, var) and
120+
simpleLocalFlowStep*(nodeTo, var) and
115121
var.asVar() = a.getInput() and
116122
nodeFrom.asCfgNode() = a.getValue()
117123
)
@@ -276,7 +282,7 @@ class TypeTracker extends TTypeTracker {
276282
result = this.append(summary)
277283
)
278284
or
279-
EssaFlow::essaFlowStep(nodeFrom, nodeTo) and
285+
typePreservingStep(nodeFrom, nodeTo) and
280286
result = this
281287
}
282288
}

python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,6 @@ module EssaFlow {
127127
nodeTo.(EssaNode).getVar() = p.getVariable() and
128128
nodeFrom.(EssaNode).getVar() = p.getAnInput()
129129
)
130-
or
131-
// Module variable read
132-
nodeFrom.(ModuleVariableNode).getARead() = nodeTo
133-
or
134-
// Module variable write
135-
nodeFrom = nodeTo.(ModuleVariableNode).getAWrite()
136130
}
137131

138132
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
@@ -153,13 +147,31 @@ module EssaFlow {
153147
* excludes SSA flow through instance fields.
154148
*/
155149
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
156-
not nodeFrom instanceof ModuleVariableNode and
157-
not nodeTo instanceof ModuleVariableNode and
158150
// If there is ESSA-flow out of a node `node`, we want flow
159151
// both out of `node` and any post-update node of `node`.
160152
exists(Node node |
161153
EssaFlow::essaFlowStep(node, nodeTo) and
162-
nodeFrom = update(node)
154+
nodeFrom = update(node) and
155+
(
156+
not node instanceof EssaNode or
157+
not nodeTo instanceof EssaNode or
158+
localEssaStep(node, nodeTo)
159+
)
160+
)
161+
}
162+
163+
/**
164+
* Holds if there is an Essa flow step from `nodeFrom` to `nodeTo` that does not switch between
165+
* local and global SSA variables.
166+
*/
167+
private predicate localEssaStep(EssaNode nodeFrom, EssaNode nodeTo) {
168+
EssaFlow::essaFlowStep(nodeFrom, nodeTo) and
169+
(
170+
nodeFrom.getVar() instanceof GlobalSsaVariable and
171+
nodeTo.getVar() instanceof GlobalSsaVariable
172+
or
173+
not nodeFrom.getVar() instanceof GlobalSsaVariable and
174+
not nodeTo.getVar() instanceof GlobalSsaVariable
163175
)
164176
}
165177

@@ -413,12 +425,11 @@ string ppReprType(DataFlowType t) { none() }
413425
* taken into account.
414426
*/
415427
predicate jumpStep(Node nodeFrom, Node nodeTo) {
416-
EssaFlow::essaFlowStep(nodeFrom, nodeTo) and
417-
(
418-
nodeFrom instanceof ModuleVariableNode
419-
or
420-
nodeTo instanceof ModuleVariableNode
421-
)
428+
// Module variable read
429+
nodeFrom.(ModuleVariableNode).getARead() = nodeTo
430+
or
431+
// Module variable write
432+
nodeFrom = nodeTo.(ModuleVariableNode).getAWrite()
422433
}
423434

424435
//--------

python/ql/test/experimental/dataflow/strange-essaflow/testFlow.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@ flowstep
44
jumpStep
55
| test.py:2:8:2:9 | GSSA Variable os | test.py:0:0:0:0 | ModuleVariableNode for Global Variable os in Module test |
66
essaFlowStep
7-
| test.py:2:8:2:9 | GSSA Variable os | test.py:0:0:0:0 | ModuleVariableNode for Global Variable os in Module test |

0 commit comments

Comments
 (0)