Skip to content

Commit ce18bff

Browse files
committed
Python: Support method calls
1 parent bd32faf commit ce18bff

File tree

4 files changed

+111
-10
lines changed

4 files changed

+111
-10
lines changed

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

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode, ArgumentNode {
5050
ArgumentPreUpdateNode() {
5151
this = any(CallNodeCall c).getArg(_)
5252
or
53+
// this = any(BoundMethodCall c).getArg(_)
54+
exists(BoundMethodCall c, int n | n > 0 | this = c.getArg(n))
55+
or
5356
this = any(SpecialCall c).getArg(_)
5457
or
5558
// Avoid argument 0 of class calls as those have non-synthetic post-update nodes.
@@ -253,8 +256,12 @@ module ArgumentPassing {
253256
*/
254257
NameNode getParameter(CallableValue callable, int n) {
255258
// positional parameter
259+
// bound method values have their positional parameters shifted.
260+
not callable instanceof BoundMethodValue and
256261
result = callable.getParameter(n)
257262
or
263+
result = callable.(BoundMethodValue).getParameter(n - 1)
264+
or
258265
// starred parameter, `*tuple`
259266
exists(Function f |
260267
f = callable.getScope() and
@@ -368,7 +375,11 @@ import ArgumentPassing
368375
* A module has no calls.
369376
*/
370377
newtype TDataFlowCallable =
371-
TCallableValue(CallableValue callable) or
378+
TCallableValue(CallableValue callable) {
379+
callable instanceof FunctionValue
380+
or
381+
callable instanceof ClassValue
382+
} or
372383
TModule(Module m)
373384

374385
/** Represents a callable. */
@@ -443,7 +454,9 @@ class DataFlowModuleScope extends DataFlowCallable, TModule {
443454
* A call corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`.
444455
*/
445456
newtype TDataFlowCall =
446-
TCallNode(CallNode call) { call = any(CallableValue c).getACall() } or
457+
TCallNode(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or
458+
/** Bound methods need to make room for the explicit self parameter */
459+
TBoundMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or
447460
TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or
448461
TSpecialCall(SpecialMethodCallNode special)
449462

@@ -471,7 +484,12 @@ abstract class DataFlowCall extends TDataFlowCall {
471484
Location getLocation() { result = this.getNode().getLocation() }
472485
}
473486

474-
/** Represents a call to a callable (currently only callable values). */
487+
/**
488+
* Represents a call to a callable (currently only callable values).
489+
* This excludes calls to bound methods, classes, and special methods.
490+
* Bound method calls and class calls insert an argument for the explicit
491+
* `self` parameter, and special method calls have special argument passing.
492+
*/
475493
class CallNodeCall extends DataFlowCall, TCallNode {
476494
CallNode call;
477495
DataFlowCallable callable;
@@ -492,7 +510,42 @@ class CallNodeCall extends DataFlowCall, TCallNode {
492510
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
493511
}
494512

495-
/** Represents a call to a class. */
513+
/**
514+
* Represents a call to a bound method call.
515+
* The node representing the instance is inserted as argument to the `self` parameter.
516+
*/
517+
class BoundMethodCall extends DataFlowCall, TBoundMethodCall {
518+
CallNode call;
519+
FunctionValue bm;
520+
521+
BoundMethodCall() {
522+
this = TBoundMethodCall(call) and
523+
call = bm.getACall()
524+
}
525+
526+
private CallableValue getCallableValue() { result = bm }
527+
528+
override string toString() { result = call.toString() }
529+
530+
override Node getArg(int n) {
531+
n > 0 and result = getArg(call, n - 1, this.getCallableValue(), n)
532+
or
533+
n = 0 and result = TCfgNode(call.getFunction().(AttrNode).getObject())
534+
}
535+
536+
override ControlFlowNode getNode() { result = call }
537+
538+
override DataFlowCallable getCallable() { result = TCallableValue(this.getCallableValue()) }
539+
540+
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
541+
}
542+
543+
/**
544+
* Represents a call to a class.
545+
* The pre-update node for the call is inserted as argument to the `self` parameter.
546+
* That makes the call node be the post-update node holding the value of the object
547+
* after the constructor has run.
548+
*/
496549
class ClassCall extends DataFlowCall, TClassCall {
497550
CallNode call;
498551
ClassValue c;

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ edges
2121
| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute |
2222
| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() |
2323
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE |
24+
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:18:56:23 | ControlFlowNode for SOURCE |
2425
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE |
26+
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:9:71:14 | ControlFlowNode for SOURCE |
2527
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE |
2628
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE |
2729
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE |
@@ -30,12 +32,21 @@ edges
3032
| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] |
3133
| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
3234
| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:18 | ControlFlowNode for Attribute |
35+
| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] |
36+
| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] |
37+
| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute |
3338
| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:17:65:17 | ControlFlowNode for x |
3439
| test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
3540
| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
3641
| test.py:65:17:65:17 | ControlFlowNode for x | test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] |
3742
| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] |
3843
| test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:67:10:67:18 | ControlFlowNode for Attribute |
44+
| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:75:22:75:22 | ControlFlowNode for x |
45+
| test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
46+
| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
47+
| test.py:75:22:75:22 | ControlFlowNode for x | test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] |
48+
| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] |
49+
| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:77:10:77:18 | ControlFlowNode for Attribute |
3950
| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] |
4051
| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] |
4152
| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | test.py:82:10:82:16 | ControlFlowNode for Attribute |
@@ -69,13 +80,24 @@ nodes
6980
| test.py:49:19:49:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
7081
| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
7182
| test.py:50:10:50:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
83+
| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post read] ControlFlowNode for myobj [Attribute foo] |
84+
| test.py:56:18:56:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
85+
| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
86+
| test.py:57:10:57:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
7287
| test.py:61:9:61:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
7388
| test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
7489
| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] |
7590
| test.py:65:17:65:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
7691
| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] |
7792
| test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] |
7893
| test.py:67:10:67:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
94+
| test.py:71:9:71:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
95+
| test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
96+
| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute() [Attribute foo] |
97+
| test.py:75:22:75:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
98+
| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] |
99+
| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] |
100+
| test.py:77:10:77:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
79101
| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] |
80102
| test.py:81:17:81:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
81103
| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
@@ -101,8 +123,12 @@ nodes
101123
| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found |
102124
| test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:50:10:50:18 | ControlFlowNode for Attribute | Flow found |
103125
| test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:50:10:50:18 | ControlFlowNode for Attribute | Flow found |
126+
| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found |
127+
| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found |
104128
| test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:67:10:67:18 | ControlFlowNode for Attribute | Flow found |
105129
| test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:67:10:67:18 | ControlFlowNode for Attribute | Flow found |
130+
| test.py:77:10:77:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:77:10:77:18 | ControlFlowNode for Attribute | Flow found |
131+
| test.py:77:10:77:18 | ControlFlowNode for Attribute | test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:77:10:77:18 | ControlFlowNode for Attribute | Flow found |
106132
| test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:82:10:82:16 | ControlFlowNode for Attribute | Flow found |
107133
| test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:82:10:82:16 | ControlFlowNode for Attribute | Flow found |
108134
| test.py:87:10:87:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:87:10:87:16 | ControlFlowNode for Attribute | Flow found |

0 commit comments

Comments
 (0)