Skip to content

Commit a22b994

Browse files
committed
Python: Revert IterableSequenceNode as LocalSourceNode
When looking things over a bit more, we could actually exclude the steps that would never be used instead. A much more involved solution, but more performance oriented and clear in terms of what is supported (at least until we start supporting type-tracking with more than depth 1 access-path, if that ever happens)
1 parent 8707a63 commit a22b994

File tree

3 files changed

+42
-9
lines changed

3 files changed

+42
-9
lines changed

python/ql/consistency-queries/TypeTrackingConsistency.ql

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ private module ConsistencyChecksInput implements ConsistencyChecksInputSig {
2727
TypeTrackingInput::simpleLocalSmallStep*(m, n)
2828
)
2929
or
30-
// TODO: when adding support for proper content, handle iterable unpacking better
31-
// such as `for k,v in items:`, or `a, (b,c) = ...`
32-
n instanceof DataFlow::IterableSequenceNode
33-
or
3430
// We have missing use-use flow in
3531
// https://github.com/python/cpython/blob/0fb18b02c8ad56299d6a2910be0bab8ad601ef24/Lib/socketserver.py#L276-L303
3632
// which I couldn't just fix. We ignore the problems here, and instead rely on the

python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ class LocalSourceNode extends Node {
7474
this instanceof ScopeEntryDefinitionNode
7575
or
7676
this instanceof ParameterNode
77-
or
78-
this instanceof IterableSequenceNode
7977
}
8078

8179
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */

python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPr
88
private import codeql.typetracking.internal.SummaryTypeTracker as SummaryTypeTracker
99
private import semmle.python.dataflow.new.internal.FlowSummaryImpl as FlowSummaryImpl
1010
private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
11+
private import semmle.python.dataflow.new.internal.IterableUnpacking as IterableUnpacking
1112

1213
private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
1314
// Dataflow nodes
@@ -135,7 +136,27 @@ module TypeTrackingInput implements Shared::TypeTrackingInput {
135136
}
136137

137138
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
138-
predicate simpleLocalSmallStep = DataFlowPrivate::simpleLocalFlowStepForTypetracking/2;
139+
predicate simpleLocalSmallStep(Node nodeFrom, Node nodeTo) {
140+
DataFlowPrivate::simpleLocalFlowStepForTypetracking(nodeFrom, nodeTo) and
141+
// for `for k,v in foo` no need to do local flow step from the synthetic sequence
142+
// node for `k,v` to the tuple `k,v` -- since type-tracking only supports one level
143+
// of content tracking, and there is one read-step from `foo` the synthetic sequence
144+
// node required, we can skip the flow step from the synthetic sequence node to the
145+
// tuple itself, since the read-step from the tuple to the tuple elements will not
146+
// matter.
147+
not (
148+
IterableUnpacking::iterableUnpackingForReadStep(_, _, nodeFrom) and
149+
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
150+
) and
151+
// for nested iterable unpacking, such as `[[a]] = foo` or `((a,b),) = bar`, we can
152+
// ignore the flow steps from the synthetic sequence node to the real sequence node,
153+
// since we only support one level of content in type-trackers, and the nested
154+
// structure requires two levels at least to be useful.
155+
not exists(SequenceNode outer |
156+
outer.getAnElement() = nodeTo.asCfgNode() and
157+
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
158+
)
159+
}
139160

140161
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
141162
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) { none() }
@@ -200,7 +221,10 @@ module TypeTrackingInput implements Shared::TypeTrackingInput {
200221
nodeTo = storeTarget
201222
or
202223
nodeTo = storeTarget.(DataFlowPrivate::SyntheticPostUpdateNode).getPreUpdateNode()
203-
)
224+
) and
225+
// when only supporting precise content, no need for IterableElementNode (since it
226+
// is only fed set/list content)
227+
not nodeFrom instanceof DataFlowPublic::IterableElementNode
204228
or
205229
TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, content)
206230
}
@@ -216,7 +240,22 @@ module TypeTrackingInput implements Shared::TypeTrackingInput {
216240
nodeTo = a
217241
)
218242
or
219-
DataFlowPrivate::readStepCommon(nodeFrom, content, nodeTo)
243+
DataFlowPrivate::readStepCommon(nodeFrom, content, nodeTo) and
244+
// Since we only support one level of content in type-trackers we don't actually
245+
// support `(aa, ab), (ba, bb) = ...`. Therefore we exclude the read-step from `(aa,
246+
// ab)` to `aa` (since it is not needed).
247+
not exists(SequenceNode outer |
248+
outer.getAnElement() = nodeFrom.asCfgNode() and
249+
IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom)
250+
) and
251+
// Again, due to only supporting one level deep, for `for (k,v) in ...` we exclude read-step from
252+
// the tuple to `k` and `v`.
253+
not exists(DataFlowPublic::IterableSequenceNode seq, DataFlowPublic::IterableElementNode elem |
254+
IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and
255+
IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and
256+
IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and
257+
nodeFrom.asCfgNode() instanceof SequenceNode
258+
)
220259
or
221260
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, content)
222261
}

0 commit comments

Comments
 (0)