@@ -1023,71 +1023,77 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
10231023 * sequence = iterable
10241024 * ```
10251025 * where `sequence` is either a tuple or a list and it can contain wildcards.
1026- * The iterable can be any iterable, which means that (CodeQL modeling of) content will need to change type
1027- * if it should be transferred from the LHS to the RHS.
1026+ * The iterable can be any iterable, which means that (CodeQL modeling of) content
1027+ * will need to change type if it should be transferred from the LHS to the RHS.
1028+ *
1029+ * Note that (CodeQL modeling of) content does not have to change type on data-flow
1030+ * path _inside_ the LHS, as the different allowed syntaxes here are merely a convenience.
1031+ * Consequently, we model all LHS sequences as tuples, which have the more precise content
1032+ * model, making flow to the elements more precise. If an element is a starred varibale,
1033+ * we will have to mutate the content type to be list content.
10281034 *
10291035 * We may for instance have
10301036 * ```python
1031- * (a, b) = ["a", "tainted string" ] # RHS has content `ListElementContent`
1037+ * (a, b) = ["a", SOURCE ] # RHS has content `ListElementContent`
10321038 * ```
1033- * Due to the abstraction for list content, we do not know whether `"tainted string" `
1039+ * Due to the abstraction for list content, we do not know whether `SOURCE `
10341040 * ends up in `a` or in `b`, so we want to overapproximate and see it in both.
10351041 *
10361042 * Using wildcards we may have
10371043 * ```python
1038- * (a, *b) = ("a", "b", "tainted string" ) # RHS has content `TupleElementContent(2)`
1044+ * (a, *b) = ("a", "b", SOURCE ) # RHS has content `TupleElementContent(2)`
10391045 * ```
1040- * Since the starred variables are always assigned type list, `*b` will be
1041- * `["b", "tainted string ]`, and we will again overapproximate and assign it
1046+ * Since the starred variables are always assigned (Python-) type list, `*b` will be
1047+ * `["b", SOURCE ]`, and we will again overapproximate and assign it
10421048 * content corresponding to anything found in the RHS.
10431049 *
10441050 * For a precise transfer
10451051 * ```python
1046- * (a, b) = ("a", "tainted string" ) # RHS has content `TupleElementContent(1)`
1052+ * (a, b) = ("a", SOURCE ) # RHS has content `TupleElementContent(1)`
10471053 * ```
10481054 * we wish to keep the precision, so only `b` receives the tuple content at index 1.
10491055 *
10501056 * Finally, `sequence` is actually a pattern and can have a more complicated structure,
10511057 * such as
10521058 * ```python
1053- * (a, [b, *c]) = ("a", ("tainted string ", "c")) # RHS has content `TupleElementContent(1); TupleElementContent(0) `
1059+ * (a, [b, *c]) = ("a", ["b ", SOURCE]) # RHS has content `TupleElementContent(1); ListElementContent `
10541060 * ```
1055- * where `a` should not receive content, but `b` and `c` should. `c` will be `["c" ]` so
1061+ * where `a` should not receive content, but `b` and `c` should. `c` will be `[SOURCE ]` so
10561062 * should have the content converted and transferred, while `b` should read it.
10571063 *
10581064 * The strategy for converting content type is to break the transfer up into a read step
10591065 * and a store step, together creating a converting transfer step.
10601066 * For this we need a synthetic node in the middle, which we call `TIterableElement(receiver)`.
1061- * It is associated with the receiver of the transfer, because we know the receiver type from the syntax.
1067+ * It is associated with the receiver of the transfer, because we know the receiver type (tuple) from the syntax.
10621068 * Since we sometimes need a converting read step (in the example above, `[b, *c]` reads the content
1063- * `TupleElementContent(0)` but should have content `ListElementContent`), we actually need a second synthetic node.
1064- * A converting read step is a read step followed by a converting transfer.
1069+ * `ListElementContent` but should have content `TupleElementContent(0)` and `TupleElementContent(0)`),
1070+ * we actually need a second synthetic node. A converting read step is a read step followed by a
1071+ * converting transfer.
1072+ *
10651073 * We can have a uniform treatment by always having two synthetic nodes and so we can view it as
10661074 * two stages of the same node. So we read into (or transfer to) `TIterableSequence(receiver)`,
10671075 * from which we take a read step to `TIterableElement(receiver)` and then a store step to `receiver`.
1076+ *
10681077 * In order to preserve precise content, we also take a flow step from `TIterableSequence(receiver)`
10691078 * directly to `receiver`.
10701079 *
1071- * The strategy is then via several read-, store-, and flow steps, illustrated on the assignment
1072- *
1073- * ```python
1074- * (a, [b, *c]) = ["a", [SOURCE]]
1075- * ```
1076- *
1080+ * The strategy is then via several read-, store-, and flow steps:
10771081 * 1. [Flow] Content is transferred from `iterable` to `TIterableSequence(sequence)` via a
10781082 * flow step. From here, everything happens on the LHS.
10791083 *
10801084 * 2. [Flow] Content is transferred from `TIterableSequence(sequence)` to `sequence` via a
10811085 * flow step.
10821086 *
10831087 * 3. [Read] Content is read from `TIterableSequence(sequence)` into `TIterableElement(sequence)`.
1084- * If `sequence` is of type tuple, we will not read tuple content as that would allow
1088+ * As `sequence` is modelled as a tuple, we will not read tuple content as that would allow
10851089 * cross talk.
10861090 *
10871091 * 4. [Store] Content is stored from `TIterableElement(sequence)` to `sequence`.
1088- * Here the content type is chosen according to the type of sequence.
1092+ * Content type is `TupleElementContent` with indices taken from the syntax.
1093+ * For instance, if `sequence` is `(a, *b, c)`, content is written to index 0, 1, and 2.
1094+ * This is adequate as the route through `TIterableElement(sequence)` does not transfer precise content.
10891095 *
1090- * 5. [Read] Content is read from `sequence` to its elements according to the type of `sequence` .
1096+ * 5. [Read] Content is read from `sequence` to its elements.
10911097 * a) If the element is a plain variable, the target is the corresponding essa node.
10921098 *
10931099 * b) If the element is itelf a sequence, with control-flow node `seq`, the target is `TIterableSequence(seq)`.
@@ -1130,7 +1136,7 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
11301136 *
11311137 * --Step 4-->
11321138 *
1133- * `[b, *c]`: [ListElementContent ]
1139+ * `[b, *c]`: [TupleElementContent(1) ]
11341140 *
11351141 * --Step 5c-->
11361142 *
@@ -1185,7 +1191,7 @@ module UnpackingAssignment {
11851191 /**
11861192 * Step 3
11871193 * Data flows from `TIterableSequence(sequence)` into `TIterableElement(sequence)`.
1188- * If `sequence` is of type tuple, we will not read tuple content as that would allow
1194+ * As `sequence` is modelled as a tuple, we will not read tuple content as that would allow
11891195 * cross talk.
11901196 */
11911197 predicate unpackingAssignmentConvertingReadStep ( Node nodeFrom , Content c , Node nodeTo ) {
@@ -1196,13 +1202,6 @@ module UnpackingAssignment {
11961202 c instanceof ListElementContent
11971203 or
11981204 c instanceof SetElementContent
1199- or
1200- // do not lose precision by routing tuple content through the `IterableElement`
1201- not target instanceof TupleNode and
1202- // `index` refers to `nodeFrom`, but only the ones in `target` are relevant.
1203- exists ( int index | exists ( target .getElement ( index ) ) |
1204- c .( TupleElementContent ) .getIndex ( ) = index
1205- )
12061205 // TODO: dict content in iterable unpacking not handled
12071206 )
12081207 )
@@ -1211,20 +1210,15 @@ module UnpackingAssignment {
12111210 /**
12121211 * Step 4
12131212 * Data flows from `TIterableElement(sequence)` to `sequence`.
1214- * The content type is chosen according to the type of sequence.
1213+ * Content type is `TupleElementContent` with indices taken from the syntax.
1214+ * For instance, if `sequence` is `(a, *b, c)`, content is written to index 0, 1, and 2.
12151215 */
12161216 predicate unpackingAssignmentConvertingStoreStep ( Node nodeFrom , Content c , Node nodeTo ) {
12171217 exists ( UnpackingAssignmentSequenceTarget target |
12181218 nodeFrom = TIterableElementNode ( target ) and
12191219 nodeTo .asCfgNode ( ) = target and
1220- (
1221- target instanceof ListNode and
1222- c instanceof ListElementContent
1223- or
1224- target instanceof TupleNode and
1225- exists ( int index | exists ( target .getElement ( index ) ) |
1226- c .( TupleElementContent ) .getIndex ( ) = index
1227- )
1220+ exists ( int index | exists ( target .getElement ( index ) ) |
1221+ c .( TupleElementContent ) .getIndex ( ) = index
12281222 )
12291223 )
12301224 }
@@ -1241,36 +1235,37 @@ module UnpackingAssignment {
12411235 */
12421236 predicate unpackingAssignmentElementReadStep ( Node nodeFrom , Content c , Node nodeTo ) {
12431237 exists (
1244- UnpackingAssignmentSequenceTarget target , int index , ControlFlowNode element , boolean precise
1238+ UnpackingAssignmentSequenceTarget target , int index , ControlFlowNode element , int starIndex
1239+ |
1240+ target .getElement ( starIndex ) instanceof StarredNode
1241+ or
1242+ not exists ( target .getAnElement ( ) .( StarredNode ) ) and
1243+ starIndex = - 1
12451244 |
12461245 nodeFrom .asCfgNode ( ) = target and
12471246 element = target .getElement ( index ) and
12481247 (
1249- target instanceof ListNode and
1250- c instanceof ListElementContent
1251- or
1252- target instanceof TupleNode and
1253- if precise = true
1248+ if starIndex = - 1 or index < starIndex
12541249 then c .( TupleElementContent ) .getIndex ( ) = index
1255- else c instanceof TupleElementContent // This could get big if big tuples exist
1250+ else
1251+ // This could get big if big tuples exist
1252+ if index = starIndex
1253+ then c .( TupleElementContent ) .getIndex ( ) >= index
1254+ else c .( TupleElementContent ) .getIndex ( ) >= index - 1
12561255 ) and
12571256 (
12581257 if element instanceof SequenceNode
12591258 then
12601259 // Step 5b
1261- nodeTo = TIterableSequenceNode ( element ) and
1262- precise = true
1260+ nodeTo = TIterableSequenceNode ( element )
12631261 else
1264- if element . getNode ( ) instanceof Starred
1262+ if element instanceof StarredNode
12651263 then
12661264 // Step 5c
1267- nodeTo = TIterableElementNode ( element ) and
1268- precise = false
1269- else (
1265+ nodeTo = TIterableElementNode ( element )
1266+ else
12701267 // Step 5a
1271- nodeTo .asVar ( ) .getDefinition ( ) .( MultiAssignmentDefinition ) .getDefiningNode ( ) = element and
1272- precise = true
1273- )
1268+ nodeTo .asVar ( ) .getDefinition ( ) .( MultiAssignmentDefinition ) .getDefiningNode ( ) = element
12741269 )
12751270 )
12761271 }
0 commit comments