Skip to content

Commit 9d2061b

Browse files
authored
Merge pull request #1669 from markshannon/python-better-handling-unknown-decorators
Python: Treat the result of calling a missing module member as 'unknown'.
2 parents 7e90728 + 4a6f385 commit 9d2061b

File tree

6 files changed

+53
-4
lines changed

6 files changed

+53
-4
lines changed

change-notes/1.22/analysis-python.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33

44
## General improvements
55

6+
### Points-to
7+
Tracking of "unknown" values from modules that are absent from the database has been improved. Particularly when an "unknown" value is used as a decorator, the decorated function is tracked.
68

79

810
### Impact on existing queries.
911

1012

11-
1213
## New queries
1314

1415
| **Query** | **Tags** | **Purpose** |

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,12 +367,10 @@ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleA
367367
}
368368

369369
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
370-
// Don't know, assume not callable.
371-
none()
370+
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
372371
}
373372

374373
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
375-
// Don't know, assume not callable.
376374
none()
377375
}
378376

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,17 @@ class ObjectInternal extends TObject {
6666

6767
/** Holds if `obj` is the result of calling `this` and `origin` is
6868
* the origin of `obj`.
69+
*
70+
* This is the context-insensitive version.
71+
* Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object.
6972
*/
7073
abstract predicate callResult(ObjectInternal obj, CfgOrigin origin);
7174

7275
/** Holds if `obj` is the result of calling `this` and `origin` is
7376
* the origin of `obj` with callee context `callee`.
77+
*
78+
* This is the context-sensitive version.
79+
* Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object.
7480
*/
7581
abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin);
7682

python/ql/test/library-tests/PointsTo/absent/Absent.expected

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,23 @@
55
| absent.py:8:1:8:4 | ControlFlowNode for open | file://:0:0:0:0 | Missing module attribute xxxx.open |
66
| absent.py:12:8:12:13 | ControlFlowNode for ImportExpr | module.py:0:0:0:0 | Module module |
77
| absent.py:14:1:14:6 | ControlFlowNode for module | module.py:0:0:0:0 | Module module |
8+
| absent.py:16:6:16:9 | ControlFlowNode for ImportExpr | file://:0:0:0:0 | Missing module xxxx |
9+
| absent.py:16:18:16:21 | ControlFlowNode for ImportMember | file://:0:0:0:0 | Missing module attribute xxxx.deco |
10+
| absent.py:18:2:18:5 | ControlFlowNode for deco | file://:0:0:0:0 | Missing module attribute xxxx.deco |
11+
| absent.py:18:2:18:5 | ControlFlowNode for deco() | absent.py:18:2:18:5 | Decorated Function func1 |
12+
| absent.py:19:1:19:12 | ControlFlowNode for FunctionExpr | absent.py:19:1:19:12 | Function func1 |
13+
| absent.py:22:2:22:5 | ControlFlowNode for deco | file://:0:0:0:0 | Missing module attribute xxxx.deco |
14+
| absent.py:22:2:22:8 | ControlFlowNode for deco()() | absent.py:22:2:22:8 | Decorated Function func2 |
15+
| absent.py:22:7:22:7 | ControlFlowNode for IntegerLiteral | file://:0:0:0:0 | int 0 |
16+
| absent.py:23:1:23:12 | ControlFlowNode for FunctionExpr | absent.py:23:1:23:12 | Function func2 |
17+
| absent.py:26:2:26:5 | ControlFlowNode for deco | file://:0:0:0:0 | Missing module attribute xxxx.deco |
18+
| absent.py:26:2:26:16 | ControlFlowNode for deco()() | absent.py:26:2:26:16 | Decorated Function func3 |
19+
| absent.py:27:1:27:12 | ControlFlowNode for FunctionExpr | absent.py:27:1:27:12 | Function func3 |
20+
| absent.py:30:2:30:5 | ControlFlowNode for deco | file://:0:0:0:0 | Missing module attribute xxxx.deco |
21+
| absent.py:30:2:30:11 | ControlFlowNode for deco()() | absent.py:30:2:30:11 | Decorated Function func4 |
22+
| absent.py:30:7:30:10 | ControlFlowNode for open | file://:0:0:0:0 | Missing module attribute xxxx.open |
23+
| absent.py:31:1:31:12 | ControlFlowNode for FunctionExpr | absent.py:31:1:31:12 | Function func4 |
24+
| absent.py:34:1:34:5 | ControlFlowNode for func1 | absent.py:18:2:18:5 | Decorated Function func1 |
25+
| absent.py:35:1:35:5 | ControlFlowNode for func2 | absent.py:22:2:22:8 | Decorated Function func2 |
26+
| absent.py:36:1:36:5 | ControlFlowNode for func3 | absent.py:26:2:26:16 | Decorated Function func3 |
27+
| absent.py:37:1:37:5 | ControlFlowNode for func4 | absent.py:30:2:30:11 | Decorated Function func4 |

python/ql/test/library-tests/PointsTo/absent/absent.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,26 @@
1212
import module
1313

1414
module
15+
16+
from xxxx import deco
17+
18+
@deco
19+
def func1():
20+
pass
21+
22+
@deco(0)
23+
def func2():
24+
pass
25+
26+
@deco(undefined)
27+
def func3():
28+
pass
29+
30+
@deco(open)
31+
def func4():
32+
pass
33+
34+
func1
35+
func2
36+
func3
37+
func4

python/ql/test/library-tests/PointsTo/decorators/Values.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
| test.py:16:22:16:25 | ControlFlowNode for func | runtime | Unknown value |
1313
| test.py:16:22:16:25 | ControlFlowNode for func | test.py:30 from import | Function func3 |
1414
| test.py:18:21:18:24 | ControlFlowNode for args | runtime | instance of tuple |
15+
| test.py:20:12:20:18 | ControlFlowNode for wrapper | runtime | Decorated Function wraps2.wrapper |
1516
| test.py:20:12:20:18 | ControlFlowNode for wrapper | test.py:30 from import | Decorated Function wraps2.wrapper |
1617
| test.py:22:2:22:9 | ControlFlowNode for annotate | import | Function annotate |
1718
| test.py:26:2:26:7 | ControlFlowNode for wraps1 | import | Function wraps1 |

0 commit comments

Comments
 (0)