Skip to content

Commit d4ea62e

Browse files
committed
Python: flow through yield
- add yield as a dataflow return - replace comprehension store step with a store step to the yield
1 parent 72530a8 commit d4ea62e

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,8 @@ class SynthCompCapturedVariablesArgumentNode extends Node, TSynthCompCapturedVar
17551755
override Scope getScope() { result = comp.getFunction() }
17561756

17571757
override Location getLocation() { result = comp.getLocation() }
1758+
1759+
Comp getComprehension() { result = comp }
17581760
}
17591761

17601762
/** Gets a viable run-time target for the call `call`. */
@@ -1796,7 +1798,10 @@ abstract class ReturnNode extends Node {
17961798
/** A data flow node that represents a value returned by a callable. */
17971799
class ExtractedReturnNode extends ReturnNode, CfgNode {
17981800
// See `TaintTrackingImplementation::returnFlowStep`
1799-
ExtractedReturnNode() { node = any(Return ret).getValue().getAFlowNode() }
1801+
ExtractedReturnNode() {
1802+
node = any(Return ret).getValue().getAFlowNode() or
1803+
node = any(Yield yield).getAFlowNode()
1804+
}
18001805

18011806
override ReturnKind getKind() { any() }
18021807
}

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,37 @@ private predicate synthDictSplatArgumentNodeStoreStep(
168168
)
169169
}
170170

171+
private predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
172+
exists(Yield yield, Function func |
173+
nodeTo.asCfgNode() = yield.getAFlowNode() and
174+
nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() and
175+
func.containsInScope(yield)
176+
|
177+
exists(Comp comp | func = comp.getFunction() |
178+
(
179+
comp instanceof ListComp or
180+
comp instanceof GeneratorExp
181+
) and
182+
c instanceof ListElementContent
183+
or
184+
comp instanceof SetComp and
185+
c instanceof SetElementContent
186+
or
187+
comp instanceof DictComp and
188+
c instanceof DictionaryElementContent
189+
)
190+
or
191+
not exists(Comp comp | func = comp.getFunction()) and
192+
(
193+
c instanceof ListElementContent
194+
or
195+
c instanceof SetElementContent
196+
or
197+
c instanceof DictionaryElementContent
198+
)
199+
)
200+
}
201+
171202
/**
172203
* Ensures that the a `**kwargs` parameter will not contain elements with names of
173204
* keyword parameters.
@@ -668,8 +699,6 @@ predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) {
668699
or
669700
setStoreStep(nodeFrom, c, nodeTo)
670701
or
671-
comprehensionStoreStep(nodeFrom, c, nodeTo)
672-
or
673702
attributeStoreStep(nodeFrom, c, nodeTo)
674703
or
675704
matchStoreStep(nodeFrom, c, nodeTo)
@@ -683,6 +712,8 @@ predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) {
683712
or
684713
synthDictSplatArgumentNodeStoreStep(nodeFrom, c, nodeTo)
685714
or
715+
yieldStoreStep(nodeFrom, c, nodeTo)
716+
or
686717
VariableCapture::storeStep(nodeFrom, c, nodeTo)
687718
}
688719

python/ql/test/library-tests/dataflow/coverage/test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def gen(x):
245245

246246
def test_yield():
247247
g = gen(SOURCE)
248-
SINK(next(g)) #$ MISSING:flow="SOURCE, l:-1 -> next()"
248+
SINK(next(g)) #$ flow="SOURCE, l:-1 -> next(..)"
249249

250250

251251
def gen_from(x):
@@ -260,7 +260,7 @@ def test_yield_from():
260260
# a statement rather than an expression, but related to generators
261261
def test_for():
262262
for x in gen(SOURCE):
263-
SINK(x) #$ MISSING:flow="SOURCE, l:-1 -> x"
263+
SINK(x) #$ flow="SOURCE, l:-1 -> x"
264264

265265

266266
# 6.2.9.1. Generator-iterator methods

0 commit comments

Comments
 (0)