Skip to content

Commit 8251553

Browse files
authored
Merge pull request #1494 from markshannon/python-better-handling-calls-on-edge-of-context
Python: better handling calls on edge of context
2 parents fad37bd + 1b98f24 commit 8251553

File tree

14 files changed

+197
-9
lines changed

14 files changed

+197
-9
lines changed

python/ql/src/semmle/python/objects/Callables.qll

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
159159
function = this and offset = 0
160160
}
161161

162+
override predicate contextSensitiveCallee() { any() }
163+
162164
}
163165

164166

@@ -280,6 +282,8 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
280282
function = this and offset = 0
281283
}
282284

285+
override predicate contextSensitiveCallee() { none() }
286+
283287
}
284288

285289
/** Class representing methods of built-in classes (otherwise known as method-descriptors) such as `list.append`.
@@ -370,6 +374,8 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod
370374
function = this and offset = 0
371375
}
372376

377+
override predicate contextSensitiveCallee() { none() }
378+
373379
}
374380

375381
/** Class representing bound-methods.
@@ -425,7 +431,6 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
425431
result = this.getFunction().getName()
426432
}
427433

428-
429434
override Function getScope() {
430435
result = this.getFunction().getScope()
431436
}
@@ -456,8 +461,9 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
456461
function = this.getFunction() and offset = 1
457462
}
458463

459-
}
460-
461-
464+
override predicate contextSensitiveCallee() {
465+
this.getFunction().contextSensitiveCallee()
466+
}
462467

468+
}
463469

python/ql/src/semmle/python/objects/Classes.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ abstract class ClassObjectInternal extends ObjectInternal {
9090

9191
override predicate subscriptUnknown() { none() }
9292

93+
override predicate contextSensitiveCallee() { none() }
94+
9395
/* Classes aren't usually iterable, but can e.g. Enums */
9496
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
9597

python/ql/src/semmle/python/objects/Constants.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ abstract class ConstantObjectInternal extends ObjectInternal {
6969

7070
override string getName() { none() }
7171

72+
override predicate contextSensitiveCallee() { none() }
73+
7274
}
7375

7476
private abstract class BooleanObjectInternal extends ConstantObjectInternal {

python/ql/src/semmle/python/objects/Descriptors.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class PropertyInternal extends ObjectInternal, TProperty {
9191
)
9292
}
9393

94+
override predicate contextSensitiveCallee() { none() }
95+
9496
/* Properties aren't iterable */
9597
override ObjectInternal getIterNext() { none() }
9698

@@ -179,6 +181,8 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
179181
result = this.getFunction().getName()
180182
}
181183

184+
override predicate contextSensitiveCallee() { none() }
185+
182186
/* Classmethods aren't iterable */
183187
override ObjectInternal getIterNext() { none() }
184188

@@ -253,6 +257,8 @@ class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
253257
result = this.getFunction().getName()
254258
}
255259

260+
override predicate contextSensitiveCallee() { none() }
261+
256262
/* Staticmethods aren't iterable */
257263
override ObjectInternal getIterNext() { none() }
258264

python/ql/src/semmle/python/objects/Instances.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ abstract class InstanceObject extends ObjectInternal {
5151

5252
override string getName() { none() }
5353

54+
override predicate contextSensitiveCallee() { none() }
55+
5456
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
5557

5658
}
@@ -368,6 +370,8 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
368370

369371
override string getName() { none() }
370372

373+
override predicate contextSensitiveCallee() { none() }
374+
371375
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
372376

373377
}
@@ -476,6 +480,8 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
476480

477481
override string getName() { none() }
478482

483+
override predicate contextSensitiveCallee() { none() }
484+
479485
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
480486

481487
}

python/ql/src/semmle/python/objects/Modules.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ abstract class ModuleObjectInternal extends ObjectInternal {
5252
any(PackageObjectInternal package).getInitModule() = this
5353
}
5454

55+
override predicate contextSensitiveCallee() { none() }
56+
5557
/* Modules aren't iterable */
5658
override ObjectInternal getIterNext() { none() }
5759

@@ -411,6 +413,8 @@ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleA
411413
/* We know what this is called, but not its innate name */
412414
override string getName() { none() }
413415

416+
override predicate contextSensitiveCallee() { none() }
417+
414418
/* Modules aren't iterable */
415419
override ObjectInternal getIterNext() { none() }
416420

python/ql/src/semmle/python/objects/ObjectInternal.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class ObjectInternal extends TObject {
167167
*/
168168
abstract string getName();
169169

170+
abstract predicate contextSensitiveCallee();
171+
170172
/** Gets the 'object' resulting from iterating over this object.
171173
* Used in the context `for i in this:`. The result is the 'object'
172174
* assigned to `i`.
@@ -256,6 +258,8 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
256258
result = this.getBuiltin().getName()
257259
}
258260

261+
override predicate contextSensitiveCallee() { none() }
262+
259263
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
260264

261265
}
@@ -335,6 +339,8 @@ class UnknownInternal extends ObjectInternal, TUnknown {
335339

336340
override string getName() { none() }
337341

342+
override predicate contextSensitiveCallee() { none() }
343+
338344
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
339345

340346
}
@@ -415,6 +421,10 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
415421

416422
override string getName() { none() }
417423

424+
/** Holds if this object requires context to determine the object resulting from a call to it.
425+
* True for most callables. */
426+
override predicate contextSensitiveCallee() { none() }
427+
418428
override ObjectInternal getIterNext() { none() }
419429

420430
}

python/ql/src/semmle/python/objects/Sequences.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ abstract class SequenceObjectInternal extends ObjectInternal {
3232

3333
override string getName() { none() }
3434

35+
override predicate contextSensitiveCallee() { none() }
36+
3537
override ObjectInternal getIterNext() { result = this.getItem(_) }
3638

3739
}

python/ql/src/semmle/python/pointsto/PointsTo.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ module InterProceduralPointsTo {
860860
)
861861
or
862862
context.untrackableCall(f) and
863+
func.contextSensitiveCallee() and
863864
value = ObjectInternal::unknown() and origin = f
864865
or
865866
exists(CfgOrigin orig |
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
1+
| 1 | ControlFlowNode for functools | Module functools | test.py:1 |
2+
| 3 | ControlFlowNode for annotate | Function annotate | test.py:3 |
3+
| 4 | ControlFlowNode for inner | Function inner | test.py:4 |
4+
| 5 | ControlFlowNode for func | Function func1 | test.py:23 |
5+
| 6 | ControlFlowNode for func | Function func1 | test.py:23 |
6+
| 7 | ControlFlowNode for inner | Function inner | test.py:4 |
7+
| 9 | ControlFlowNode for wraps1 | Function wraps1 | test.py:9 |
8+
| 10 | ControlFlowNode for args | args | test.py:10 |
9+
| 10 | ControlFlowNode for wrapper | Function wrapper | test.py:10 |
10+
| 11 | ControlFlowNode for args | args | test.py:10 |
11+
| 13 | ControlFlowNode for wrapper | Function wrapper | test.py:10 |
12+
| 15 | ControlFlowNode for wraps2 | Function wraps2 | test.py:15 |
13+
| 16 | ControlFlowNode for func | Function func3 | test.py:31 |
14+
| 16 | ControlFlowNode for functools | Module functools | test.py:1 |
15+
| 17 | ControlFlowNode for args | args | test.py:17 |
16+
| 17 | ControlFlowNode for wrapper | Function wrapper | test.py:17 |
17+
| 18 | ControlFlowNode for args | args | test.py:17 |
18+
| 20 | ControlFlowNode for wrapper | Function wrapper | test.py:17 |
19+
| 22 | ControlFlowNode for annotate | Function annotate | test.py:3 |
20+
| 23 | ControlFlowNode for func1 | Function func1 | test.py:23 |
21+
| 26 | ControlFlowNode for wraps1 | Function wraps1 | test.py:9 |
22+
| 27 | ControlFlowNode for func2 | Function wrapper | test.py:10 |
23+
| 30 | ControlFlowNode for wraps2 | Function wraps2 | test.py:15 |
24+
| 31 | ControlFlowNode for func3 | Function wrapper | test.py:17 |
125
| 41 | ControlFlowNode for func1 | Function func1 | test.py:23 |
226
| 42 | ControlFlowNode for func2 | Function wrapper | test.py:10 |
327
| 43 | ControlFlowNode for func3 | Function wrapper | test.py:17 |
28+
| 48 | ControlFlowNode for None | NoneType None | test.py:48 |
29+
| 48 | ControlFlowNode for register | Function register | test.py:48 |
30+
| 49 | ControlFlowNode for decorator | Function decorator | test.py:49 |
31+
| 50 | ControlFlowNode for callable | Builtin-function callable | test.py:50 |
32+
| 50 | ControlFlowNode for func | Function baz | test.py:72 |
33+
| 50 | ControlFlowNode for func | Function foo | test.py:60 |
34+
| 51 | ControlFlowNode for ValueError | builtin-class ValueError | test.py:51 |
35+
| 52 | ControlFlowNode for func | Function baz | test.py:72 |
36+
| 52 | ControlFlowNode for func | Function foo | test.py:60 |
37+
| 54 | ControlFlowNode for callable | Builtin-function callable | test.py:54 |
38+
| 54 | ControlFlowNode for name | Function bar | test.py:66 |
39+
| 54 | ControlFlowNode for name | NoneType None | test.py:48 |
40+
| 54 | ControlFlowNode for name | int 17 | test.py:59 |
41+
| 55 | ControlFlowNode for decorator | Function decorator | test.py:49 |
42+
| 55 | ControlFlowNode for name | Function bar | test.py:66 |
43+
| 57 | ControlFlowNode for decorator | Function decorator | test.py:49 |
44+
| 59 | ControlFlowNode for register | Function register | test.py:48 |
45+
| 60 | ControlFlowNode for foo | Function foo | test.py:60 |
46+
| 63 | ControlFlowNode for foo | Function foo | test.py:60 |
47+
| 65 | ControlFlowNode for register | Function register | test.py:48 |
48+
| 66 | ControlFlowNode for bar | Function bar | test.py:66 |
49+
| 69 | ControlFlowNode for bar | Function bar | test.py:66 |
50+
| 71 | ControlFlowNode for register | Function register | test.py:48 |
51+
| 72 | ControlFlowNode for baz | Function baz | test.py:72 |
52+
| 75 | ControlFlowNode for baz | Function baz | test.py:72 |

0 commit comments

Comments
 (0)