Skip to content

Commit 5f189a7

Browse files
committed
Python: Address reviews
1 parent 1edad03 commit 5f189a7

2 files changed

Lines changed: 90 additions & 23 deletions

File tree

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

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,6 @@ module EssaFlow {
135135
nodeTo = TIterableSequence(target)
136136
)
137137
or
138-
exists(Assign assign, SequenceNode target | target.getNode() = assign.getATarget() |
139-
nodeFrom.asExpr() = assign.getValue() and
140-
nodeTo.asCfgNode() = target
141-
)
142-
or
143138
// With definition
144139
// `with f(42) as x:`
145140
// nodeFrom is `f(42)`, cfg node
@@ -153,6 +148,10 @@ module EssaFlow {
153148
contextManager.strictlyDominates(var)
154149
)
155150
or
151+
// Paramter definition
152+
// `def foo(x):`
153+
// nodeFrom is `x`, cfgNode
154+
// nodeTo is `x`, essa var
156155
exists(ParameterDefinition pd |
157156
nodeFrom.asCfgNode() = pd.getDefiningNode() and
158157
nodeTo.asVar() = pd.getVariable()
@@ -175,6 +174,7 @@ module EssaFlow {
175174
// If expressions
176175
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
177176
or
177+
// Flow inside an unpacking assignment
178178
unpackingAssignmentFlowStep(nodeFrom, nodeTo)
179179
or
180180
// Overflow keyword argument
@@ -1039,7 +1039,7 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
10391039
*
10401040
* We may for instance have
10411041
* ```python
1042-
* (a, b) = ["a", "tainted string"] # RHS has content `ListElement`
1042+
* (a, b) = ["a", "tainted string"] # RHS has content `ListElementContent`
10431043
* ```
10441044
* Due to the abstraction for list content, we do not know whether `"tainted string"`
10451045
* ends up in `a` or in `b`, so we want to overapproximate and see it in both.
@@ -1054,14 +1054,14 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
10541054
*
10551055
* For a precise transfer
10561056
* ```python
1057-
* (a, b) = ("a", "tainted string") # RHS has content `TupleElement(1)`
1057+
* (a, b) = ("a", "tainted string") # RHS has content `TupleElementContent(1)`
10581058
* ```
10591059
* we wish to keep the precision, so only `b` receives the tuple content at index 1.
10601060
*
10611061
* Finally, `sequence` is actually a pattern and can have a more complicated structure,
10621062
* such as
10631063
* ```python
1064-
* (a, [b, *c]) = ("a", ("tainted string", "c")) # RHS has content `TupleElement(1); TupleElement(0)`
1064+
* (a, [b, *c]) = ("a", ("tainted string", "c")) # RHS has content `TupleElementContent(1); TupleElementContent(0)`
10651065
* ```
10661066
* where `a` should not receive content, but `b` and `c` should. `c` will be `["c"]` so
10671067
* should have the content converted and transferred, while `b` should read it.
@@ -1071,15 +1071,20 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
10711071
* For this we need a synthetic node in the middle, which we call `TIterableElement(receiver)`.
10721072
* It is associated with the receiver of the transfer, because we know the receiver type from the syntax.
10731073
* Since we sometimes need a converting read step (in the example above, `[b, *c]` reads the content
1074-
* `TupleElement(0)` but should have content `ListElement`), we actually need a second synthetic node.
1074+
* `TupleElementContent(0)` but should have content `ListElementContent`), we actually need a second synthetic node.
10751075
* A converting read step is a read step followed by a converting transfer.
10761076
* We can have a uniform treatment by always having two synthetic nodes and so we can view it as
10771077
* two stages of the same node. So we read into (or transfer to) `TIterableSequence(receiver)`,
10781078
* from which we take a read step to `TIterableElement(receiver)` and then a store step to `receiver`.
10791079
* In order to preserve precise content, we also take a flow step from `TIterableSequence(receiver)`
10801080
* directly to `receiver`.
10811081
*
1082-
* The strategy is then via several read-, store-, and flow steps:
1082+
* The strategy is then via several read-, store-, and flow steps, illustrated on the assignment
1083+
*
1084+
* ```python
1085+
* (a, [b, *c]) = ["a", [SOURCE]]
1086+
* ```
1087+
*
10831088
* 1. [Flow] Content is transferred from `iterable` to `TIterableSequence(sequence)` via a
10841089
* flow step. From here, everything happens on the LHS.
10851090
*
@@ -1094,17 +1099,61 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
10941099
* Here the content type is chosen according to the type of sequence.
10951100
*
10961101
* 5. [Read] Content is read from `sequence` to its elements according to the type of `sequence`.
1097-
* If the element is a plain variable, the target is the corresponding essa node.
1098-
* If the element is itelf a sequence, with control-flow node `seq`, the target is `TIterableSequence(seq)`.
1099-
* If the element is a starred variable, with control-flow node `v`, the target is `TIterableElement(v)`.
1102+
* a) If the element is a plain variable, the target is the corresponding essa node.
1103+
*
1104+
* b) If the element is itelf a sequence, with control-flow node `seq`, the target is `TIterableSequence(seq)`.
1105+
*
1106+
* c) If the element is a starred variable, with control-flow node `v`, the target is `TIterableElement(v)`.
11001107
*
11011108
* 6. [Store] Content is stored from `TIterableElement(v)` to the essa variable for `v`, with
1102-
* content type `ListElement`.
1109+
* content type `ListElementContent`.
1110+
* (We will see this in step 7)
1111+
*
1112+
* 7. [Flow, Read, Store] Steps 2 through 7 are repeated for all recursive elements which are sequences.
1113+
*
1114+
*
1115+
* We illustrate the above steps on the assignment
1116+
* ```python
1117+
* (a, [b, *c]) = ["a", [SOURCE]]
1118+
* ```
1119+
* where the path to `c` is
1120+
*
1121+
* `["a", [SOURCE]]`: [ListElementContent; ListElementContent]
1122+
*
1123+
* --Step 1-->
11031124
*
1104-
* 7. [Flow, Read, Store] The last 5 steps are repeated for all recursive elements which are sequences.
1125+
* `TIterableSequence((a, [b, *c]))`: [ListElementContent; ListElementContent]
1126+
*
1127+
* --Step 3-->
1128+
*
1129+
* `TIterableElement((a, [b, *c]))`: [ListElementContent]
1130+
*
1131+
* --Step 4-->
1132+
*
1133+
* `(a, [b, *c])`: [TupleElementContent(1); ListElementContent]
1134+
*
1135+
* --Step 5b-->
1136+
*
1137+
* `TIterableSequence([b, *c])`: [ListElementContent]
1138+
*
1139+
* --Step 3-->
1140+
*
1141+
* `TIterableElement([b, *c])`: []
1142+
*
1143+
* --Step 4-->
1144+
*
1145+
* `[b, *c]`: [ListElementContent]
1146+
*
1147+
* --Step 5c-->
1148+
*
1149+
* `TIterableElement(c)`: []
1150+
*
1151+
* --Step 6-->
1152+
*
1153+
* `c`: [ListElementContent]
11051154
*/
11061155
module UnpackingAssignment {
1107-
/** A direct (or top-level) target of an unpacking assignment */
1156+
/** A direct (or top-level) target of an unpacking assignment. */
11081157
class UnpackingAssignmentDirectTarget extends ControlFlowNode {
11091158
Expr value;
11101159

@@ -1116,7 +1165,7 @@ module UnpackingAssignment {
11161165
Expr getValue() { result = value }
11171166
}
11181167

1119-
/** A (possibly recursive) target of an unpacking assignment */
1168+
/** A (possibly recursive) target of an unpacking assignment. */
11201169
class UnpackingAssignmentTarget extends ControlFlowNode {
11211170
UnpackingAssignmentTarget() {
11221171
this instanceof UnpackingAssignmentDirectTarget
@@ -1129,15 +1178,22 @@ module UnpackingAssignment {
11291178
ControlFlowNode getAnElement() { result = this.getElement(_) }
11301179
}
11311180

1181+
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
1182+
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget {
1183+
UnpackingAssignmentSequenceTarget() { this instanceof SequenceNode }
1184+
}
1185+
1186+
/** Step 2 */
11321187
predicate unpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
1133-
exists(UnpackingAssignmentTarget target | target instanceof SequenceNode |
1188+
exists(UnpackingAssignmentSequenceTarget target |
11341189
nodeFrom = TIterableSequence(target) and
11351190
nodeTo.asCfgNode() = target
11361191
)
11371192
}
11381193

1194+
/** Step 3 */
11391195
predicate unpackingAssignmentConvertingReadStep(Node nodeFrom, Content c, Node nodeTo) {
1140-
exists(UnpackingAssignmentTarget target | target instanceof SequenceNode |
1196+
exists(UnpackingAssignmentSequenceTarget target |
11411197
nodeFrom = TIterableSequence(target) and
11421198
nodeTo = TIterableElement(target) and
11431199
(
@@ -1156,8 +1212,9 @@ module UnpackingAssignment {
11561212
)
11571213
}
11581214

1215+
/** Step 4 */
11591216
predicate unpackingAssignmentConvertingStoreStep(Node nodeFrom, Content c, Node nodeTo) {
1160-
exists(UnpackingAssignmentTarget target | target instanceof SequenceNode |
1217+
exists(UnpackingAssignmentSequenceTarget target |
11611218
nodeFrom = TIterableElement(target) and
11621219
nodeTo.asCfgNode() = target and
11631220
(
@@ -1172,6 +1229,7 @@ module UnpackingAssignment {
11721229
)
11731230
}
11741231

1232+
/** Step 5 */
11751233
predicate unpackingAssignmentElementReadStep(Node nodeFrom, Content c, Node nodeTo) {
11761234
exists(UnpackingAssignmentTarget target, int index, ControlFlowNode element, boolean precise |
11771235
target instanceof SequenceNode
@@ -1190,21 +1248,25 @@ module UnpackingAssignment {
11901248
(
11911249
if element instanceof SequenceNode
11921250
then
1251+
// Step 5b
11931252
nodeTo = TIterableSequence(element) and
11941253
precise = true
11951254
else
11961255
if element.getNode() instanceof Starred
11971256
then
1257+
// Step 5c
11981258
nodeTo = TIterableElement(element) and
11991259
precise = false
12001260
else (
1261+
// Step 5a
12011262
nodeTo.asVar().getDefinition().(MultiAssignmentDefinition).getDefiningNode() = element and
12021263
precise = true
12031264
)
12041265
)
12051266
)
12061267
}
12071268

1269+
/** Step 6 */
12081270
predicate unpackingAssignmentStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) {
12091271
exists(ControlFlowNode starred | starred.getNode() instanceof Starred |
12101272
nodeFrom = TIterableElement(starred) and
@@ -1213,13 +1275,14 @@ module UnpackingAssignment {
12131275
)
12141276
}
12151277

1216-
/** Data flows from an iterable to an assigned variable. */
1278+
/** All read steps associated with unpacking assignment. */
12171279
predicate unpackingAssignmentReadStep(Node nodeFrom, Content c, Node nodeTo) {
12181280
unpackingAssignmentElementReadStep(nodeFrom, c, nodeTo)
12191281
or
12201282
unpackingAssignmentConvertingReadStep(nodeFrom, c, nodeTo)
12211283
}
12221284

1285+
/** All store steps associated with unpacking assignment. */
12231286
predicate unpackingAssignmentStoreStep(Node nodeFrom, Content c, Node nodeTo) {
12241287
unpackingAssignmentStarredElementStoreStep(nodeFrom, c, nodeTo)
12251288
or

python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,10 @@ class KwUnpacked extends Node, TKwUnpacked {
330330

331331
/**
332332
* A synthetic node representing an iterable sequence. Used for changing content type
333-
* for instance from a `ListElement` to a `TupleElement`.
333+
* for instance from a `ListElement` to a `TupleElement`, especially if the content is
334+
* transferred via a read step which cannot be broken up into a read and a store. The
335+
* read step then targets TIterableSequence, and the conversion can happen via a read
336+
* step to TIterableElement followed by a store step to the target.
334337
*/
335338
class IterableSequence extends Node, TIterableSequence {
336339
SequenceNode consumer;
@@ -348,7 +351,8 @@ class IterableSequence extends Node, TIterableSequence {
348351

349352
/**
350353
* A synthetic node representing an iterable element. Used for changing content type
351-
* for instance from a `ListElement` to a `TupleElement`.
354+
* for instance from a `ListElement` to a `TupleElement`. This would happen via a
355+
* read step from the list to IterableElement followed by a store step to the tuple.
352356
*/
353357
class IterableElement extends Node, TIterableElement {
354358
ControlFlowNode consumer;

0 commit comments

Comments
 (0)