Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions python/ql/src/semmle/python/dataflow/SsaDefinitions.qll
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,15 @@ class EscapingGlobalVariable extends ModuleVariable {

}

class EscapingAssignmentGlobalVariable extends EscapingGlobalVariable {

EscapingAssignmentGlobalVariable() {
exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope())
}

}


class SpecialSsaSourceVariable extends PythonSsaSourceVariable {

SpecialSsaSourceVariable() {
Expand Down
23 changes: 16 additions & 7 deletions python/ql/src/semmle/python/pointsto/PointsTo.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1713,14 +1713,23 @@ module PointsTo {
*/
pragma [noinline]
private predicate callsite_points_to(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
exists(EssaVariable var, PointsToContext callee |
Flow::callsite_exit_value_transfer(var, callee, def, context) and
ssa_variable_points_to(var, callee, value, cls, origin)
exists(SsaSourceVariable srcvar |
srcvar = def.getSourceVariable() |
if srcvar instanceof EscapingAssignmentGlobalVariable then (
/* If global variable can be reassigned, we need to track it through calls */
exists(EssaVariable var, PointsToContext callee |
Flow::callsite_exit_value_transfer(var, callee, def, context) and
ssa_variable_points_to(var, callee, value, cls, origin)
)
or
callsite_points_to_python(def, context, value, cls, origin)
or
callsite_points_to_builtin(def, context, value, cls, origin)
) else (
/* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
)
)
or
callsite_points_to_python(def, context, value, cls, origin)
or
callsite_points_to_builtin(def, context, value, cls, origin)
}

pragma [noinline]
Expand Down
45 changes: 42 additions & 3 deletions python/ql/test/library-tests/PointsTo/new/Dataflow.expected
Original file line number Diff line number Diff line change
Expand Up @@ -625,31 +625,40 @@
| r_regressions.py:0 | __name___0 = ScopeEntryDefinition |
| r_regressions.py:0 | __package___0 = ScopeEntryDefinition |
| r_regressions.py:0 | _names_0 = ScopeEntryDefinition |
| r_regressions.py:0 | _names_3 = Pi(_names_1) [false] |
| r_regressions.py:0 | _names_4 = phi(_names_2, _names_3) |
| r_regressions.py:0 | gv_0 = ScopeEntryDefinition |
| r_regressions.py:0 | mod_gv_0 = ScopeEntryDefinition |
| r_regressions.py:0 | sys_0 = ScopeEntryDefinition |
| r_regressions.py:0 | t_0 = ScopeEntryDefinition |
| r_regressions.py:0 | t_2 = phi(t_0, t_1) |
| r_regressions.py:5 | Queue_0 = ClassExpr |
| r_regressions.py:7 | __init___0 = FunctionExpr |
| r_regressions.py:7 | gv_5 = ScopeEntryDefinition |
| r_regressions.py:7 | self_0 = ParameterDefinition |
| r_regressions.py:9 | gv_6 = CallsiteRefinement(gv_5) |
| r_regressions.py:9 | self_1 = SelfCallsiteRefinement(self_0) |
| r_regressions.py:11 | _after_fork_0 = FunctionExpr |
| r_regressions.py:11 | gv_7 = ScopeEntryDefinition |
| r_regressions.py:11 | self_0 = ParameterDefinition |
| r_regressions.py:12 | self_1 = AttributeAssignment '_closed'(self_0) |
| r_regressions.py:13 | self_2 = AttributeAssignment '_close'(self_1) |
| r_regressions.py:15 | close_0 = FunctionExpr |
| r_regressions.py:15 | close_4 = Pi(close_0) [false] |
| r_regressions.py:15 | close_5 = phi(close_3, close_4) |
| r_regressions.py:15 | gv_8 = ScopeEntryDefinition |
| r_regressions.py:15 | gv_33 = phi(gv_9, gv_11) |
| r_regressions.py:15 | self_0 = ParameterDefinition |
| r_regressions.py:15 | self_3 = phi(self_1, self_2) |
| r_regressions.py:16 | self_1 = AttributeAssignment '_closed'(self_0) |
| r_regressions.py:18 | gv_9 = CallsiteRefinement(gv_8) |
| r_regressions.py:20 | close_0 = Attribute |
| r_regressions.py:20 | close_1 = Attribute |
| r_regressions.py:20 | gv_10 = phi(gv_8, gv_9) |
| r_regressions.py:21 | close_2 = SingleSuccessorGuard(close_1) [true] |
| r_regressions.py:22 | close_3 = Pi(close_0) [true] |
| r_regressions.py:22 | self_2 = AttributeAssignment '_close'(self_1) |
| r_regressions.py:23 | gv_11 = CallsiteRefinement(gv_9) |
| r_regressions.py:27 | f_0 = FunctionExpr |
| r_regressions.py:27 | gv_12 = ScopeEntryDefinition |
| r_regressions.py:27 | gv_33 = phi(gv_12, gv_13) |
| r_regressions.py:27 | x_0 = ParameterDefinition |
| r_regressions.py:27 | x_5 = phi(x_3, x_4) |
| r_regressions.py:27 | y_0 = ParameterDefinition |
Expand All @@ -664,60 +673,90 @@
| r_regressions.py:33 | y_1 = Pi(y_0) [false] |
| r_regressions.py:33 | y_2 = phi(y_0, y_1) |
| r_regressions.py:36 | y_3 = Pi(y_2) [true] |
| r_regressions.py:39 | gv_13 = CallsiteRefinement(gv_12) |
| r_regressions.py:39 | x_4 = phi(x_1, x_3) |
| r_regressions.py:39 | y_4 = Pi(y_0) [true] |
| r_regressions.py:39 | y_5 = phi(y_3, y_4) |
| r_regressions.py:39 | y_6 = ArgumentRefinement(y_5) |
| r_regressions.py:39 | z_1 = Pi(z_0) [true] |
| r_regressions.py:39 | z_2 = phi(z_0, z_1) |
| r_regressions.py:42 | find_library_0 = FunctionExpr |
| r_regressions.py:42 | gv_14 = ScopeEntryDefinition |
| r_regressions.py:42 | name_0 = ParameterDefinition |
| r_regressions.py:43 | __0 = ... |
| r_regressions.py:43 | data_0 = ... |
| r_regressions.py:43 | gv_15 = CallsiteRefinement(gv_14) |
| r_regressions.py:46 | fail_0 = FunctionExpr |
| r_regressions.py:46 | gv_16 = ScopeEntryDefinition |
| r_regressions.py:46 | msg_0 = ParameterDefinition |
| r_regressions.py:49 | C_0 = ClassExpr |
| r_regressions.py:51 | fail_0 = FunctionExpr |
| r_regressions.py:51 | fail_1 = ScopeEntryDefinition |
| r_regressions.py:51 | gv_17 = ScopeEntryDefinition |
| r_regressions.py:51 | msg_0 = ParameterDefinition |
| r_regressions.py:51 | self_0 = ParameterDefinition |
| r_regressions.py:52 | gv_18 = CallsiteRefinement(gv_17) |
| r_regressions.py:52 | msg_1 = ArgumentRefinement(msg_0) |
| r_regressions.py:58 | decorator_0 = ParameterDefinition |
| r_regressions.py:58 | gv_19 = ScopeEntryDefinition |
| r_regressions.py:58 | method_decorator_0 = FunctionExpr |
| r_regressions.py:58 | name_0 = ParameterDefinition |
| r_regressions.py:61 | _dec_0 = FunctionExpr |
| r_regressions.py:61 | func_0 = ScopeEntryDefinition |
| r_regressions.py:61 | gv_20 = ScopeEntryDefinition |
| r_regressions.py:61 | gv_33 = phi(gv_23, gv_25) |
| r_regressions.py:61 | is_class_6 = phi(is_class_4, is_class_5) |
| r_regressions.py:61 | name_1 = ScopeEntryDefinition |
| r_regressions.py:61 | obj_0 = ParameterDefinition |
| r_regressions.py:61 | obj_3 = phi(obj_1, obj_2) |
| r_regressions.py:62 | gv_21 = CallsiteRefinement(gv_20) |
| r_regressions.py:62 | is_class_0 = isinstance() |
| r_regressions.py:62 | obj_1 = ArgumentRefinement(obj_0) |
| r_regressions.py:64 | gv_22 = CallsiteRefinement(gv_21) |
| r_regressions.py:64 | is_class_1 = Pi(is_class_0) [true] |
| r_regressions.py:66 | func_1 = obj |
| r_regressions.py:66 | is_class_2 = Pi(is_class_0) [false] |
| r_regressions.py:68 | _wrapper_0 = FunctionExpr |
| r_regressions.py:68 | args_0 = ParameterDefinition |
| r_regressions.py:68 | func_2 = phi(func_0, func_1) |
| r_regressions.py:68 | gv_23 = phi(gv_21, gv_22) |
| r_regressions.py:68 | gv_24 = ScopeEntryDefinition |
| r_regressions.py:68 | is_class_3 = phi(is_class_1, is_class_2) |
| r_regressions.py:68 | kwargs_0 = ParameterDefinition |
| r_regressions.py:68 | self_0 = ParameterDefinition |
| r_regressions.py:73 | gv_25 = CallsiteRefinement(gv_23) |
| r_regressions.py:73 | is_class_4 = Pi(is_class_3) [true] |
| r_regressions.py:73 | obj_2 = ArgumentRefinement(obj_1) |
| r_regressions.py:76 | is_class_5 = Pi(is_class_3) [false] |
| r_regressions.py:80 | deco_0 = FunctionExpr |
| r_regressions.py:80 | func_0 = ParameterDefinition |
| r_regressions.py:80 | gv_26 = ScopeEntryDefinition |
| r_regressions.py:81 | _wrapper_0 = FunctionExpr |
| r_regressions.py:81 | args_0 = ParameterDefinition |
| r_regressions.py:81 | gv_27 = ScopeEntryDefinition |
| r_regressions.py:81 | kwargs_0 = ParameterDefinition |
| r_regressions.py:85 | deco_1 = ArgumentRefinement(deco_0) |
| r_regressions.py:85 | gv_1 = CallsiteRefinement(gv_0) |
| r_regressions.py:85 | gv_2 = CallsiteRefinement(gv_1) |
| r_regressions.py:86 | TestFirst_1 = method_decorator()() |
| r_regressions.py:87 | gv_28 = ScopeEntryDefinition |
| r_regressions.py:87 | method_0 = FunctionExpr |
| r_regressions.py:87 | self_0 = ParameterDefinition |
| r_regressions.py:90 | gv_3 = CallsiteRefinement(gv_2) |
| r_regressions.py:90 | gv_4 = CallsiteRefinement(gv_3) |
| r_regressions.py:93 | sys_1 = ImportExpr |
| r_regressions.py:95 | _names_1 = Attribute |
| r_regressions.py:98 | _names_2 = Pi(_names_1) [true] |
| r_regressions.py:98 | t_1 = ImportExpr |
| r_regressions.py:100 | _names_3 = Pi(_names_1) [false] |
| r_regressions.py:100 | _names_4 = phi(_names_2, _names_3) |
| r_regressions.py:100 | gv_29 = C() |
| r_regressions.py:100 | t_2 = phi(t_0, t_1) |
| r_regressions.py:102 | gv_30 = CallsiteRefinement(gv_29) |
| r_regressions.py:106 | gv_31 = ScopeEntryDefinition |
| r_regressions.py:106 | mod_gv_1 = FunctionExpr |
| r_regressions.py:106 | x_0 = ParameterDefinition |
| r_regressions.py:107 | gv_32 = AttributeAssignment 'attr'(gv_31) |
| s_scopes.py:0 | __name___0 = ScopeEntryDefinition |
| s_scopes.py:0 | __package___0 = ScopeEntryDefinition |
| s_scopes.py:0 | float_0 = ScopeEntryDefinition |
Expand Down
2 changes: 2 additions & 0 deletions python/ql/test/library-tests/PointsTo/new/NameSpace.expected
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@
| r_regressions.py:0 | Module code.r_regressions | f | Function f |
| r_regressions.py:0 | Module code.r_regressions | fail | Function fail |
| r_regressions.py:0 | Module code.r_regressions | find_library | Function find_library |
| r_regressions.py:0 | Module code.r_regressions | gv | C() |
| r_regressions.py:0 | Module code.r_regressions | method_decorator | Function method_decorator |
| r_regressions.py:0 | Module code.r_regressions | mod_gv | Function mod_gv |
| r_regressions.py:0 | Module code.r_regressions | sys | Module sys |
| r_regressions.py:0 | Module code.r_regressions | t | Module time |
| r_regressions.py:5 | Class Queue | __init__ | Function __init__ |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,7 @@
| r_regressions.py:64 | ControlFlowNode for do_validation() | 64 |
| r_regressions.py:90 | ControlFlowNode for Attribute | 90 |
| r_regressions.py:90 | ControlFlowNode for Attribute() | 90 |
| r_regressions.py:102 | ControlFlowNode for unrelated_call | 102 |
| r_regressions.py:102 | ControlFlowNode for unrelated_call() | 102 |
| r_regressions.py:107 | ControlFlowNode for Attribute | 106 |
| r_regressions.py:107 | ControlFlowNode for x | 106 |
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,13 @@
| r_regressions.py:97 | ControlFlowNode for _names | tuple object | builtin-class tuple | 95 | import |
| r_regressions.py:98 | ControlFlowNode for ImportExpr | Module time | builtin-class module | 98 | import |
| r_regressions.py:98 | ControlFlowNode for t | Module time | builtin-class module | 98 | import |
| r_regressions.py:100 | ControlFlowNode for C | class C | builtin-class type | 49 | import |
| r_regressions.py:100 | ControlFlowNode for C() | C() | class C | 100 | import |
| r_regressions.py:100 | ControlFlowNode for gv | C() | class C | 100 | import |
| r_regressions.py:104 | ControlFlowNode for gv | C() | class C | 100 | import |
| r_regressions.py:106 | ControlFlowNode for FunctionExpr | Function mod_gv | builtin-class function | 106 | import |
| r_regressions.py:106 | ControlFlowNode for mod_gv | Function mod_gv | builtin-class function | 106 | import |
| r_regressions.py:107 | ControlFlowNode for gv | C() | class C | 100 | runtime |
| s_scopes.py:4 | ControlFlowNode for True | bool True | builtin-class bool | 4 | import |
| s_scopes.py:4 | ControlFlowNode for float | bool True | builtin-class bool | 4 | import |
| s_scopes.py:7 | ControlFlowNode for C2 | class C2 | builtin-class type | 7 | import |
Expand Down
2 changes: 2 additions & 0 deletions python/ql/test/library-tests/PointsTo/new/SSA.expected
Original file line number Diff line number Diff line change
Expand Up @@ -486,10 +486,12 @@
| n_nesting.py:10 | C_5 = ScopeEntryDefinition | int 1 | builtin-class int |
| n_nesting.py:10 | compile_ops_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* |
| n_nesting.py:10 | inner_0 = FunctionExpr | Function inner | builtin-class function |
| n_nesting.py:11 | C_6 = CallsiteRefinement(C_5) | int 1 | builtin-class int |
| n_nesting.py:13 | C_7 = ScopeEntryDefinition | int 1 | builtin-class int |
| n_nesting.py:13 | compile_ops_4 = Pi(compile_ops_1) [false] | bool True | builtin-class bool |
| n_nesting.py:13 | compile_ops_5 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* |
| n_nesting.py:13 | inner_1 = FunctionExpr | Function inner | builtin-class function |
| n_nesting.py:14 | C_8 = CallsiteRefinement(C_7) | int 1 | builtin-class int |
| n_nesting.py:15 | attrs_0 = Dict | Dict | builtin-class dict |
| n_nesting.py:16 | compile_ops_6 = phi(compile_ops_2, compile_ops_4) | bool True | builtin-class bool |
| n_nesting.py:16 | inner_2 = phi(inner_0, inner_1) | Function inner | builtin-class function |
Expand Down
11 changes: 11 additions & 0 deletions python/ql/test/library-tests/PointsTo/new/code/r_regressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,14 @@ def method(self):

if 'time' in _names:
import time as t

gv = C()

unrelated_call()

gv

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this PR, we would not have a value for gv here.


def mod_gv(x):
gv.attr = x