Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 29a162b

Browse files
committed
Python: Proper flow **arg -> **param
1 parent b0ed7af commit 29a162b

5 files changed

Lines changed: 52 additions & 21 deletions

File tree

python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,13 @@ module EssaFlow {
170170
nodeTo.(EssaNode).getVar() = p.getVariable() and
171171
nodeFrom.(EssaNode).getVar() = p.getAnInput()
172172
)
173+
or
174+
// Overflow keyword argument
175+
exists(CallNode call, CallableValue callable |
176+
call = callable.getACall() and
177+
nodeTo = TKwOverflowNode(call, callable) and
178+
nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode()
179+
)
173180
}
174181

175182
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
@@ -300,11 +307,6 @@ module ArgumentPassing {
300307
call_unpacks(call, callable, name, n) and
301308
result = TKwUnpacked(call, callable, name)
302309
)
303-
or
304-
// Dict argument is passed to the doubly starred parameter (at position -2).
305-
// This is an overaaproximation, not removing unpacked arguments.
306-
n = -2 and
307-
result = TCfgNode(call.getNode().getKwargs().getAFlowNode())
308310
)
309311
}
310312

@@ -342,7 +344,8 @@ module ArgumentPassing {
342344
f = callable.getScope() and
343345
not exists(call.getArg(n)) and // no positional arguement available
344346
name = f.getArgName(n) and
345-
not exists(call.getArgByName(name)) and // no keyword argument available
347+
// not exists(call.getArgByName(name)) and // only matches keyword arguments not preceded by **
348+
not call.getNode().getANamedArg().(Keyword).getArg() = name and // no keyword argument available
346349
n >= 0 and
347350
n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and
348351
exists(call.getNode().getKwargs()) // dict argument available
@@ -912,7 +915,13 @@ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node no
912915
* in `x.f = newValue`.
913916
*/
914917
cached
915-
predicate clearsContent(Node n, Content c) { none() }
918+
predicate clearsContent(Node n, Content c) {
919+
exists(CallNode call, CallableValue callable, string name |
920+
call_unpacks(call, callable, name, _) and
921+
n = TKwOverflowNode(call, callable) and
922+
c.(DictionaryElementContent).getKey() = name
923+
)
924+
}
916925

917926
//--------
918927
// Fancy context-sensitive guards

python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ newtype TNode =
3636
/** A node representing the overflow keyword arguments to a call. */
3737
TKwOverflowNode(CallNode call, CallableValue callable) {
3838
exists(getKeywordOverflowArg(call, callable, _))
39+
or
40+
exists(call.getNode().getKwargs()) and
41+
callable.getScope().hasKwArg()
3942
} or
4043
/**
4144
* A node representing an unpacked element of a dictionary argument.
@@ -252,6 +255,26 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
252255
override Location getLocation() { result = mod.getLocation() }
253256
}
254257

258+
class PosOverflowNode extends Node, TPosOverflowNode {
259+
CallNode call;
260+
261+
PosOverflowNode() { this = TPosOverflowNode(call, _) }
262+
263+
override string toString() { result = "PosOverflowNode for " + call.getNode().toString() }
264+
265+
override Location getLocation() { result = call.getLocation() }
266+
}
267+
268+
class KwOverflowNode extends Node, TKwOverflowNode {
269+
CallNode call;
270+
271+
KwOverflowNode() { this = TKwOverflowNode(call, _) }
272+
273+
override string toString() { result = "KwOverflowNode for " + call.getNode().toString() }
274+
275+
override Location getLocation() { result = call.getLocation() }
276+
}
277+
255278
/**
256279
* A node that controls whether other nodes are evaluated.
257280
*/

python/ql/test/experimental/dataflow/coverage/argumentPassing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def grab_foo_bar_baz(foo, **kwargs):
122122
def grab_bar_baz(bar, **kwargs):
123123
SINK2(bar)
124124
try:
125-
SINK2(kwargs["bar"]) # FP
125+
SINK2(kwargs["bar"])
126126
except:
127127
print("OK")
128128
grab_baz(**kwargs)

python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
| argumentPassing.py:97:29:97:32 | ControlFlowNode for arg2 | argumentPassing.py:91:11:91:11 | ControlFlowNode for b |
44
| argumentPassing.py:112:30:112:33 | ControlFlowNode for arg2 | argumentPassing.py:104:11:104:11 | ControlFlowNode for b |
55
| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:123:11:123:13 | ControlFlowNode for bar |
6-
| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:125:15:125:27 | ControlFlowNode for Subscript |
76
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key |
87
| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key |
98
| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key |

python/ql/test/experimental/dataflow/coverage/dataflow.expected

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ edges
3030
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute |
3131
| file://:0:0:0:0 | Data flow node | test.py:381:10:381:43 | ControlFlowNode for second() |
3232
| file://:0:0:0:0 | Data flow node | test.py:477:10:477:43 | ControlFlowNode for second() |
33-
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() |
34-
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() |
35-
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() |
36-
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() |
3733
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE |
3834
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE |
3935
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:9:87:14 | ControlFlowNode for SOURCE |
@@ -164,15 +160,19 @@ edges
164160
| test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() |
165161
| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node |
166162
| test.py:381:36:381:41 | ControlFlowNode for SOURCE | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] |
167-
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] |
168-
| test.py:397:39:397:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] |
163+
| test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() |
164+
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
165+
| test.py:397:10:397:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() |
166+
| test.py:397:39:397:44 | ControlFlowNode for SOURCE | test.py:397:10:397:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
169167
| test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() |
170168
| test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() |
171169
| test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() |
172170
| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node |
173171
| test.py:477:36:477:41 | ControlFlowNode for SOURCE | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] |
174-
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] |
175-
| test.py:487:39:487:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] |
172+
| test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() |
173+
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
174+
| test.py:487:10:487:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() |
175+
| test.py:487:39:487:44 | ControlFlowNode for SOURCE | test.py:487:10:487:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
176176
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a |
177177
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b |
178178
nodes
@@ -197,10 +197,6 @@ nodes
197197
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
198198
| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node |
199199
| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node |
200-
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] |
201-
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] |
202-
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] |
203-
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] |
204200
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test |
205201
| test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
206202
| test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
@@ -328,8 +324,10 @@ nodes
328324
| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
329325
| test.py:381:36:381:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
330326
| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
327+
| test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
331328
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
332329
| test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
330+
| test.py:397:10:397:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
333331
| test.py:397:39:397:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
334332
| test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
335333
| test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
@@ -341,8 +339,10 @@ nodes
341339
| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
342340
| test.py:477:36:477:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
343341
| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
342+
| test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
344343
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
345344
| test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
345+
| test.py:487:10:487:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
346346
| test.py:487:39:487:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
347347
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
348348
| test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |

0 commit comments

Comments
 (0)