Skip to content

Commit 0f95992

Browse files
committed
Python: remove NonLibraryDataFlowCallable
this required managing parameters and their pre-update nodes a bit
1 parent fa2da2f commit 0f95992

File tree

5 files changed

+50
-32
lines changed

5 files changed

+50
-32
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,9 @@ module ArgumentPassing {
103103
* Used to limit the size of predicates.
104104
*/
105105
predicate connects(CallNode call, CallableValue callable) {
106-
exists(NormalCall c, NonLibraryDataFlowCallable k |
106+
exists(NormalCall c |
107107
call = c.getNode() and
108-
callable = k.getCallableValue() and
109-
k = c.getCallable()
108+
callable = c.getCallable().getCallableValue()
110109
)
111110
}
112111

@@ -322,31 +321,24 @@ class DataFlowCallable extends TDataFlowCallable {
322321
/** Gets the name of this callable. */
323322
string getName() { none() }
324323

324+
/** Gets a callable value for this callable, if any. */
325+
CallableValue getCallableValue() { none() }
326+
325327
/** Gets the underlying library callable, if any. */
326328
LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) }
327329

328330
Location getLocation() { none() }
329331
}
330332

331-
/** A callable that is not synthesised. Either a CallableValue, a lambda or a module (only used to provide scopes for module variables). */
332-
abstract class NonLibraryDataFlowCallable extends DataFlowCallable {
333-
/** Gets a callable value for this callable, if one exists. */
334-
abstract CallableValue getCallableValue();
335-
336-
abstract CallNode getANonLibraryCall();
337-
338-
final override CallNode getACall() { result = this.getANonLibraryCall() }
339-
}
340-
341333
/** A class representing a callable value. */
342-
class DataFlowCallableValue extends NonLibraryDataFlowCallable, TCallableValue {
334+
class DataFlowCallableValue extends DataFlowCallable, TCallableValue {
343335
CallableValue callable;
344336

345337
DataFlowCallableValue() { this = TCallableValue(callable) }
346338

347339
override string toString() { result = callable.toString() }
348340

349-
override CallNode getANonLibraryCall() { result = callable.getACall() }
341+
override CallNode getACall() { result = callable.getACall() }
350342

351343
override Scope getScope() { result = callable.getScope() }
352344

@@ -358,14 +350,14 @@ class DataFlowCallableValue extends NonLibraryDataFlowCallable, TCallableValue {
358350
}
359351

360352
/** A class representing a callable lambda. */
361-
class DataFlowLambda extends NonLibraryDataFlowCallable, TLambda {
353+
class DataFlowLambda extends DataFlowCallable, TLambda {
362354
Function lambda;
363355

364356
DataFlowLambda() { this = TLambda(lambda) }
365357

366358
override string toString() { result = lambda.toString() }
367359

368-
override CallNode getANonLibraryCall() { result = this.getCallableValue().getACall() }
360+
override CallNode getACall() { result = this.getCallableValue().getACall() }
369361

370362
override Scope getScope() { result = lambda.getEvaluatingScope() }
371363

@@ -381,14 +373,14 @@ class DataFlowLambda extends NonLibraryDataFlowCallable, TLambda {
381373
}
382374

383375
/** A class representing the scope in which a `ModuleVariableNode` appears. */
384-
class DataFlowModuleScope extends NonLibraryDataFlowCallable, TModule {
376+
class DataFlowModuleScope extends DataFlowCallable, TModule {
385377
Module mod;
386378

387379
DataFlowModuleScope() { this = TModule(mod) }
388380

389381
override string toString() { result = mod.toString() }
390382

391-
override CallNode getANonLibraryCall() { none() }
383+
override CallNode getACall() { none() }
392384

393385
override Scope getScope() { result = mod }
394386

@@ -525,7 +517,7 @@ class NormalCall extends DataFlowSourceCall, TNormalCall {
525517
* `self` parameter, and special method calls have special argument passing.
526518
*/
527519
class FunctionCall extends NormalCall {
528-
NonLibraryDataFlowCallable callable;
520+
DataFlowCallableValue callable;
529521

530522
FunctionCall() {
531523
call = any(FunctionValue f).getAFunctionCall() and
@@ -539,7 +531,7 @@ class FunctionCall extends NormalCall {
539531

540532
/** A call to a lambda. */
541533
class LambdaCall extends NormalCall {
542-
NonLibraryDataFlowCallable callable;
534+
DataFlowLambda callable;
543535

544536
LambdaCall() {
545537
call = callable.getACall() and
@@ -695,6 +687,19 @@ class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
695687
}
696688

697689
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = sc }
690+
691+
override string toString() { result = "parameter " + pos + " of " + sc }
692+
693+
// Hack to return "empty location"
694+
override predicate hasLocationInfo(
695+
string file, int startline, int startcolumn, int endline, int endcolumn
696+
) {
697+
file = "" and
698+
startline = 0 and
699+
startcolumn = 0 and
700+
endline = 0 and
701+
endcolumn = 0
702+
}
698703
}
699704

700705
/** A data-flow node used to model flow summaries. */
@@ -707,6 +712,17 @@ class SummaryNode extends Node, TSummaryNode {
707712
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
708713

709714
override string toString() { result = "[summary] " + state + " in " + c }
715+
716+
// Hack to return "empty location"
717+
override predicate hasLocationInfo(
718+
string file, int startline, int startcolumn, int endline, int endcolumn
719+
) {
720+
file = "" and
721+
startline = 0 and
722+
startcolumn = 0 and
723+
endline = 0 and
724+
endcolumn = 0
725+
}
710726
}
711727

712728
private class SummaryReturnNode extends SummaryNode, ReturnNode {

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ module SyntheticPostUpdateNode {
139139
Node argumentPreUpdateNode() {
140140
result = any(FunctionCall c).getArg(_)
141141
or
142+
result = any(LambdaCall c).getArg(_)
143+
or
142144
// Avoid argument 0 of method calls as those have read post-update nodes.
143145
exists(MethodCall c, int n | n > 0 | result = c.getArg(n))
144146
or
@@ -153,14 +155,11 @@ module SyntheticPostUpdateNode {
153155
)
154156
}
155157

156-
predicate resolvedCall(CallNode call) {
157-
call = any(FunctionValue f).getAFunctionCall()
158-
or
159-
call = any(FunctionValue f).getAMethodCall()
160-
or
161-
call = any(ClassValue c | not c.isAbsent()).getACall()
158+
/** Holds if `call` can be resolved as anormal call */
159+
private predicate resolvedCall(CallNode call) {
160+
call = any(DataFlowCallableValue cv).getACall()
162161
or
163-
call instanceof SpecialMethodCallNode
162+
call = any(DataFlowLambda l).getACall()
164163
}
165164

166165
/** Gets the pre-update node associated with a store. This is used for when an object might have its value changed after a store. */

python/ql/src/Security/CWE-020-ExternalAPIs/ExternalAPIs.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ deprecated class SafeExternalAPI = SafeExternalApi;
5050

5151
/** The default set of "safe" external APIs. */
5252
private class DefaultSafeExternalApi extends SafeExternalApi {
53-
override DataFlowPrivate::NonLibraryDataFlowCallable getSafeCallable() {
53+
override DataFlowPrivate::DataFlowCallable getSafeCallable() {
5454
exists(CallableValue cv | cv = result.getCallableValue() |
5555
cv = Value::named(["len", "isinstance", "getattr", "hasattr"])
5656
or
@@ -65,7 +65,7 @@ private class DefaultSafeExternalApi extends SafeExternalApi {
6565

6666
/** A node representing data being passed to an external API through a call. */
6767
class ExternalApiDataNode extends DataFlow::Node {
68-
DataFlowPrivate::NonLibraryDataFlowCallable callable;
68+
DataFlowPrivate::DataFlowCallable callable;
6969
int i;
7070

7171
ExternalApiDataNode() {
@@ -156,7 +156,7 @@ class ExternalApiUsedWithUntrustedData extends TExternalApi {
156156
/** Gets a textual representation of this element. */
157157
string toString() {
158158
exists(
159-
DataFlowPrivate::NonLibraryDataFlowCallable callable, int index, string callableString,
159+
DataFlowPrivate::DataFlowCallable callable, int index, string callableString,
160160
string indexString
161161
|
162162
this = TExternalApiParameter(callable, index) and

python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ abstract class RoutingTest extends InlineExpectationsTest {
5252
result =
5353
toNode
5454
.getEnclosingCallable()
55-
.(DataFlowPrivate::NonLibraryDataFlowCallable)
55+
.(DataFlowPrivate::DataFlowCallable)
5656
.getCallableValue()
5757
.getScope()
5858
.getQualifiedName() // TODO: More robust pretty printing?

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
| classes.py:14:17:14:60 | ControlFlowNode for Attribute() | classes.py:14:17:14:60 | ControlFlowNode for Attribute() |
2+
| classes.py:14:33:14:59 | ControlFlowNode for Attribute() | classes.py:14:33:14:59 | ControlFlowNode for Attribute() |
13
| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() |
24
| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self |
35
| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() |
46
| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() |
57
| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() |
8+
| classes.py:412:29:412:52 | ControlFlowNode for dict() | classes.py:412:29:412:52 | ControlFlowNode for dict() |
69
| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript |
710
| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self |
811
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key |

0 commit comments

Comments
 (0)