Python: Proper flow **arg -> **param

This commit is contained in:
Rasmus Lerchedahl Petersen
2020-09-30 23:55:02 +02:00
parent b0ed7af897
commit 29a162bc9c
5 changed files with 52 additions and 21 deletions

View File

@@ -170,6 +170,13 @@ module EssaFlow {
nodeTo.(EssaNode).getVar() = p.getVariable() and
nodeFrom.(EssaNode).getVar() = p.getAnInput()
)
or
// Overflow keyword argument
exists(CallNode call, CallableValue callable |
call = callable.getACall() and
nodeTo = TKwOverflowNode(call, callable) and
nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode()
)
}
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
@@ -300,11 +307,6 @@ module ArgumentPassing {
call_unpacks(call, callable, name, n) and
result = TKwUnpacked(call, callable, name)
)
or
// Dict argument is passed to the doubly starred parameter (at position -2).
// This is an overaaproximation, not removing unpacked arguments.
n = -2 and
result = TCfgNode(call.getNode().getKwargs().getAFlowNode())
)
}
@@ -342,7 +344,8 @@ module ArgumentPassing {
f = callable.getScope() and
not exists(call.getArg(n)) and // no positional arguement available
name = f.getArgName(n) and
not exists(call.getArgByName(name)) and // no keyword argument available
// not exists(call.getArgByName(name)) and // only matches keyword arguments not preceded by **
not call.getNode().getANamedArg().(Keyword).getArg() = name and // no keyword argument available
n >= 0 and
n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and
exists(call.getNode().getKwargs()) // dict argument available
@@ -912,7 +915,13 @@ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node no
* in `x.f = newValue`.
*/
cached
predicate clearsContent(Node n, Content c) { none() }
predicate clearsContent(Node n, Content c) {
exists(CallNode call, CallableValue callable, string name |
call_unpacks(call, callable, name, _) and
n = TKwOverflowNode(call, callable) and
c.(DictionaryElementContent).getKey() = name
)
}
//--------
// Fancy context-sensitive guards

View File

@@ -36,6 +36,9 @@ newtype TNode =
/** A node representing the overflow keyword arguments to a call. */
TKwOverflowNode(CallNode call, CallableValue callable) {
exists(getKeywordOverflowArg(call, callable, _))
or
exists(call.getNode().getKwargs()) and
callable.getScope().hasKwArg()
} or
/**
* A node representing an unpacked element of a dictionary argument.
@@ -252,6 +255,26 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
override Location getLocation() { result = mod.getLocation() }
}
class PosOverflowNode extends Node, TPosOverflowNode {
CallNode call;
PosOverflowNode() { this = TPosOverflowNode(call, _) }
override string toString() { result = "PosOverflowNode for " + call.getNode().toString() }
override Location getLocation() { result = call.getLocation() }
}
class KwOverflowNode extends Node, TKwOverflowNode {
CallNode call;
KwOverflowNode() { this = TKwOverflowNode(call, _) }
override string toString() { result = "KwOverflowNode for " + call.getNode().toString() }
override Location getLocation() { result = call.getLocation() }
}
/**
* A node that controls whether other nodes are evaluated.
*/

View File

@@ -122,7 +122,7 @@ def grab_foo_bar_baz(foo, **kwargs):
def grab_bar_baz(bar, **kwargs):
SINK2(bar)
try:
SINK2(kwargs["bar"]) # FP
SINK2(kwargs["bar"])
except:
print("OK")
grab_baz(**kwargs)

View File

@@ -3,7 +3,6 @@
| argumentPassing.py:97:29:97:32 | ControlFlowNode for arg2 | argumentPassing.py:91:11:91:11 | ControlFlowNode for b |
| argumentPassing.py:112:30:112:33 | ControlFlowNode for arg2 | argumentPassing.py:104:11:104:11 | ControlFlowNode for b |
| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:123:11:123:13 | ControlFlowNode for bar |
| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:125:15:125:27 | ControlFlowNode for Subscript |
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key |
| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key |
| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key |

View File

@@ -30,10 +30,6 @@ edges
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute |
| file://:0:0:0:0 | Data flow node | test.py:381:10:381:43 | ControlFlowNode for second() |
| file://:0:0:0:0 | Data flow node | test.py:477:10:477:43 | ControlFlowNode for second() |
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() |
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE |
| 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
| test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() |
| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node |
| test.py:381:36:381:41 | ControlFlowNode for SOURCE | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] |
| test.py:397:39:397:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] |
| 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() |
| 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] |
| 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() |
| 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] |
| test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() |
| test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() |
| test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() |
| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node |
| test.py:477:36:477:41 | ControlFlowNode for SOURCE | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] |
| test.py:487:39:487:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] |
| 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() |
| 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] |
| 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() |
| 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] |
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a |
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b |
nodes
@@ -197,10 +197,6 @@ nodes
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node |
| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node |
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] |
| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test |
| test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
@@ -328,8 +324,10 @@ nodes
| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:381:36:381:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
| 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] |
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
| 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] |
| test.py:397:39:397:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
| test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
@@ -341,8 +339,10 @@ nodes
| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:477:36:477:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
| 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] |
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
| 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] |
| test.py:487:39:487:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |