Skip to content

Commit e1bf282

Browse files
committed
Python: split variable capture instantiation out
into its own file.
1 parent f668453 commit e1bf282

File tree

2 files changed

+190
-191
lines changed

2 files changed

+190
-191
lines changed

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

Lines changed: 1 addition & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ private import semmle.python.Frameworks
1717
import MatchUnpacking
1818
import IterableUnpacking
1919
import DataFlowDispatch
20+
import VariableCapture as VariableCapture
2021

2122
/** Gets the callable in which this node occurs. */
2223
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
@@ -384,197 +385,6 @@ module LocalFlow {
384385
}
385386
}
386387

387-
/** Provides logic related to captured variables. */
388-
module VariableCapture {
389-
private import codeql.dataflow.VariableCapture as Shared
390-
391-
private module CaptureInput implements Shared::InputSig<Location> {
392-
private import python as PY
393-
394-
additional class ExprCfgNode extends ControlFlowNode {
395-
ExprCfgNode() { isExpressionNode(this) }
396-
}
397-
398-
private predicate closureFlowStep(ExprCfgNode nodeFrom, ExprCfgNode nodeTo) {
399-
// TODO: Other languages have an extra case here looking like
400-
// simpleAstFlowStep(nodeFrom, nodeTo)
401-
// we should investigate the potential benefit of adding that.
402-
exists(SsaVariable def |
403-
def.getAUse() = nodeTo and
404-
def.getAnUltimateDefinition().getDefinition().(DefinitionNode).getValue() = nodeFrom
405-
)
406-
}
407-
408-
class Callable extends Scope {
409-
predicate isConstructor() { none() }
410-
}
411-
412-
class BasicBlock extends PY::BasicBlock {
413-
Callable getEnclosingCallable() { result = this.getScope() }
414-
415-
// TODO: check that this gives useful results
416-
Location getLocation() { result = super.getNode(0).getLocation() }
417-
}
418-
419-
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
420-
result = bb.getImmediateDominator()
421-
}
422-
423-
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
424-
425-
class CapturedVariable extends LocalVariable {
426-
Function f;
427-
428-
CapturedVariable() {
429-
// note: captured variables originating on module scope is currently
430-
// covered by global variable handling.
431-
this.getScope() = f and
432-
this.getAnAccess().getScope() != f
433-
}
434-
435-
Callable getCallable() { result = f }
436-
437-
Location getLocation() { result = f.getLocation() }
438-
439-
/** Gets a scope that captures this variable. */
440-
Scope getACapturingScope() {
441-
result = this.getAnAccess().getScope().getScope*() and
442-
result.getScope+() = f
443-
}
444-
}
445-
446-
class CapturedParameter extends CapturedVariable {
447-
CapturedParameter() { this.isParameter() }
448-
449-
ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
450-
}
451-
452-
class Expr extends ExprCfgNode {
453-
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
454-
}
455-
456-
class VariableWrite extends ControlFlowNode {
457-
CapturedVariable v;
458-
459-
VariableWrite() { this = v.getAStore().getAFlowNode() }
460-
461-
CapturedVariable getVariable() { result = v }
462-
463-
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
464-
}
465-
466-
class VariableRead extends Expr {
467-
CapturedVariable v;
468-
469-
VariableRead() { this = v.getALoad().getAFlowNode() }
470-
471-
CapturedVariable getVariable() { result = v }
472-
}
473-
474-
class ClosureExpr extends Expr {
475-
ClosureExpr() {
476-
this.getNode() instanceof CallableExpr
477-
or
478-
this.getNode() instanceof Comp
479-
}
480-
481-
predicate hasBody(Callable body) {
482-
body = this.getNode().(CallableExpr).getInnerScope()
483-
or
484-
body = this.getNode().(Comp).getFunction()
485-
}
486-
487-
predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) }
488-
}
489-
}
490-
491-
class CapturedVariable = CaptureInput::CapturedVariable;
492-
493-
class ClosureExpr = CaptureInput::ClosureExpr;
494-
495-
module Flow = Shared::Flow<Location, CaptureInput>;
496-
497-
private Flow::ClosureNode asClosureNode(Node n) {
498-
result = n.(SynthCaptureNode).getSynthesizedCaptureNode()
499-
or
500-
result.(Flow::ExprNode).getExpr() = n.(CfgNode).getNode()
501-
or
502-
result.(Flow::VariableWriteSourceNode).getVariableWrite() = n.(CfgNode).getNode()
503-
or
504-
result.(Flow::ExprPostUpdateNode).getExpr() =
505-
n.(PostUpdateNode).getPreUpdateNode().(CfgNode).getNode()
506-
or
507-
result.(Flow::ParameterNode).getParameter().getCfgNode() = n.(CfgNode).getNode()
508-
or
509-
result.(Flow::ThisParameterNode).getCallable() =
510-
n.(SynthCapturingClosureParameterNode).getCallable()
511-
}
512-
513-
predicate storeStep(Node nodeFrom, CapturedVariableContent c, Node nodeTo) {
514-
Flow::storeStep(asClosureNode(nodeFrom), c.getVariable(), asClosureNode(nodeTo))
515-
}
516-
517-
predicate readStep(Node nodeFrom, CapturedVariableContent c, Node nodeTo) {
518-
Flow::readStep(asClosureNode(nodeFrom), c.getVariable(), asClosureNode(nodeTo))
519-
}
520-
521-
predicate valueStep(Node nodeFrom, Node nodeTo) {
522-
Flow::localFlowStep(asClosureNode(nodeFrom), asClosureNode(nodeTo))
523-
}
524-
525-
// Note: Learn from JS, https://github.com/github/codeql/pull/14412
526-
// - JS: Capture flow
527-
// - JS: Disallow consecutive captured contents
528-
private module Debug {
529-
predicate flowStoreStep(
530-
Node nodeFrom, Flow::ClosureNode closureNodeFrom, CapturedVariable v,
531-
Flow::ClosureNode closureNodeTo, Node nodeTo
532-
) {
533-
closureNodeFrom = asClosureNode(nodeFrom) and
534-
closureNodeTo = asClosureNode(nodeTo) and
535-
Flow::storeStep(closureNodeFrom, v, closureNodeTo)
536-
}
537-
538-
predicate unmappedFlowStoreStep(
539-
Flow::ClosureNode closureNodeFrom, CapturedVariable v, Flow::ClosureNode closureNodeTo
540-
) {
541-
Flow::storeStep(closureNodeFrom, v, closureNodeTo) and
542-
not flowStoreStep(_, closureNodeFrom, v, closureNodeTo, _)
543-
}
544-
545-
predicate flowReadStep(
546-
Node nodeFrom, Flow::ClosureNode closureNodeFrom, CapturedVariable v,
547-
Flow::ClosureNode closureNodeTo, Node nodeTo
548-
) {
549-
closureNodeFrom = asClosureNode(nodeFrom) and
550-
closureNodeTo = asClosureNode(nodeTo) and
551-
Flow::readStep(closureNodeFrom, v, closureNodeTo)
552-
}
553-
554-
predicate unmappedFlowReadStep(
555-
Flow::ClosureNode closureNodeFrom, CapturedVariable v, Flow::ClosureNode closureNodeTo
556-
) {
557-
Flow::readStep(closureNodeFrom, v, closureNodeTo) and
558-
not flowReadStep(_, closureNodeFrom, v, closureNodeTo, _)
559-
}
560-
561-
predicate flowValueStep(
562-
Node nodeFrom, Flow::ClosureNode closureNodeFrom, Flow::ClosureNode closureNodeTo, Node nodeTo
563-
) {
564-
closureNodeFrom = asClosureNode(nodeFrom) and
565-
closureNodeTo = asClosureNode(nodeTo) and
566-
Flow::localFlowStep(closureNodeFrom, closureNodeTo)
567-
}
568-
569-
predicate unmappedFlowValueStep(
570-
Flow::ClosureNode closureNodeFrom, Flow::ClosureNode closureNodeTo
571-
) {
572-
Flow::localFlowStep(closureNodeFrom, closureNodeTo) and
573-
not flowValueStep(_, closureNodeFrom, closureNodeTo, _)
574-
}
575-
}
576-
}
577-
578388
//--------
579389
// Local flow
580390
//--------
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/** Provides logic related to captured variables. */
2+
3+
private import python
4+
private import DataFlowPublic
5+
private import semmle.python.dataflow.new.internal.DataFlowPrivate
6+
private import codeql.dataflow.VariableCapture as Shared
7+
8+
private module CaptureInput implements Shared::InputSig<Location> {
9+
private import python as PY
10+
11+
additional class ExprCfgNode extends ControlFlowNode {
12+
ExprCfgNode() { isExpressionNode(this) }
13+
}
14+
15+
private predicate closureFlowStep(ExprCfgNode nodeFrom, ExprCfgNode nodeTo) {
16+
// TODO: Other languages have an extra case here looking like
17+
// simpleAstFlowStep(nodeFrom, nodeTo)
18+
// we should investigate the potential benefit of adding that.
19+
exists(SsaVariable def |
20+
def.getAUse() = nodeTo and
21+
def.getAnUltimateDefinition().getDefinition().(DefinitionNode).getValue() = nodeFrom
22+
)
23+
}
24+
25+
class Callable extends Scope {
26+
predicate isConstructor() { none() }
27+
}
28+
29+
class BasicBlock extends PY::BasicBlock {
30+
Callable getEnclosingCallable() { result = this.getScope() }
31+
32+
// TODO: check that this gives useful results
33+
Location getLocation() { result = super.getNode(0).getLocation() }
34+
}
35+
36+
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
37+
38+
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
39+
40+
class CapturedVariable extends LocalVariable {
41+
Function f;
42+
43+
CapturedVariable() {
44+
// note: captured variables originating on module scope is currently
45+
// covered by global variable handling.
46+
this.getScope() = f and
47+
this.getAnAccess().getScope() != f
48+
}
49+
50+
Callable getCallable() { result = f }
51+
52+
Location getLocation() { result = f.getLocation() }
53+
54+
/** Gets a scope that captures this variable. */
55+
Scope getACapturingScope() {
56+
result = this.getAnAccess().getScope().getScope*() and
57+
result.getScope+() = f
58+
}
59+
}
60+
61+
class CapturedParameter extends CapturedVariable {
62+
CapturedParameter() { this.isParameter() }
63+
64+
ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
65+
}
66+
67+
class Expr extends ExprCfgNode {
68+
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
69+
}
70+
71+
class VariableWrite extends ControlFlowNode {
72+
CapturedVariable v;
73+
74+
VariableWrite() { this = v.getAStore().getAFlowNode() }
75+
76+
CapturedVariable getVariable() { result = v }
77+
78+
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
79+
}
80+
81+
class VariableRead extends Expr {
82+
CapturedVariable v;
83+
84+
VariableRead() { this = v.getALoad().getAFlowNode() }
85+
86+
CapturedVariable getVariable() { result = v }
87+
}
88+
89+
class ClosureExpr extends Expr {
90+
ClosureExpr() {
91+
this.getNode() instanceof CallableExpr
92+
or
93+
this.getNode() instanceof Comp
94+
}
95+
96+
predicate hasBody(Callable body) {
97+
body = this.getNode().(CallableExpr).getInnerScope()
98+
or
99+
body = this.getNode().(Comp).getFunction()
100+
}
101+
102+
predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) }
103+
}
104+
}
105+
106+
class CapturedVariable = CaptureInput::CapturedVariable;
107+
108+
class ClosureExpr = CaptureInput::ClosureExpr;
109+
110+
module Flow = Shared::Flow<Location, CaptureInput>;
111+
112+
private Flow::ClosureNode asClosureNode(Node n) {
113+
result = n.(SynthCaptureNode).getSynthesizedCaptureNode()
114+
or
115+
result.(Flow::ExprNode).getExpr() = n.(CfgNode).getNode()
116+
or
117+
result.(Flow::VariableWriteSourceNode).getVariableWrite() = n.(CfgNode).getNode()
118+
or
119+
result.(Flow::ExprPostUpdateNode).getExpr() =
120+
n.(PostUpdateNode).getPreUpdateNode().(CfgNode).getNode()
121+
or
122+
result.(Flow::ParameterNode).getParameter().getCfgNode() = n.(CfgNode).getNode()
123+
or
124+
result.(Flow::ThisParameterNode).getCallable() =
125+
n.(SynthCapturingClosureParameterNode).getCallable()
126+
}
127+
128+
predicate storeStep(Node nodeFrom, CapturedVariableContent c, Node nodeTo) {
129+
Flow::storeStep(asClosureNode(nodeFrom), c.getVariable(), asClosureNode(nodeTo))
130+
}
131+
132+
predicate readStep(Node nodeFrom, CapturedVariableContent c, Node nodeTo) {
133+
Flow::readStep(asClosureNode(nodeFrom), c.getVariable(), asClosureNode(nodeTo))
134+
}
135+
136+
predicate valueStep(Node nodeFrom, Node nodeTo) {
137+
Flow::localFlowStep(asClosureNode(nodeFrom), asClosureNode(nodeTo))
138+
}
139+
140+
/**
141+
* Provides predicates to understand the behaviour of the variable capture
142+
* library instantiation on Python code bases.
143+
*/
144+
private module Debug {
145+
predicate flowStoreStep(
146+
Node nodeFrom, Flow::ClosureNode closureNodeFrom, CapturedVariable v,
147+
Flow::ClosureNode closureNodeTo, Node nodeTo
148+
) {
149+
closureNodeFrom = asClosureNode(nodeFrom) and
150+
closureNodeTo = asClosureNode(nodeTo) and
151+
Flow::storeStep(closureNodeFrom, v, closureNodeTo)
152+
}
153+
154+
predicate unmappedFlowStoreStep(
155+
Flow::ClosureNode closureNodeFrom, CapturedVariable v, Flow::ClosureNode closureNodeTo
156+
) {
157+
Flow::storeStep(closureNodeFrom, v, closureNodeTo) and
158+
not flowStoreStep(_, closureNodeFrom, v, closureNodeTo, _)
159+
}
160+
161+
predicate flowReadStep(
162+
Node nodeFrom, Flow::ClosureNode closureNodeFrom, CapturedVariable v,
163+
Flow::ClosureNode closureNodeTo, Node nodeTo
164+
) {
165+
closureNodeFrom = asClosureNode(nodeFrom) and
166+
closureNodeTo = asClosureNode(nodeTo) and
167+
Flow::readStep(closureNodeFrom, v, closureNodeTo)
168+
}
169+
170+
predicate unmappedFlowReadStep(
171+
Flow::ClosureNode closureNodeFrom, CapturedVariable v, Flow::ClosureNode closureNodeTo
172+
) {
173+
Flow::readStep(closureNodeFrom, v, closureNodeTo) and
174+
not flowReadStep(_, closureNodeFrom, v, closureNodeTo, _)
175+
}
176+
177+
predicate flowValueStep(
178+
Node nodeFrom, Flow::ClosureNode closureNodeFrom, Flow::ClosureNode closureNodeTo, Node nodeTo
179+
) {
180+
closureNodeFrom = asClosureNode(nodeFrom) and
181+
closureNodeTo = asClosureNode(nodeTo) and
182+
Flow::localFlowStep(closureNodeFrom, closureNodeTo)
183+
}
184+
185+
predicate unmappedFlowValueStep(Flow::ClosureNode closureNodeFrom, Flow::ClosureNode closureNodeTo) {
186+
Flow::localFlowStep(closureNodeFrom, closureNodeTo) and
187+
not flowValueStep(_, closureNodeFrom, closureNodeTo, _)
188+
}
189+
}

0 commit comments

Comments
 (0)