Skip to content

Commit 0627fad

Browse files
authored
Merge pull request #2669 from RasmusWL/python-modernise-resources
Python: modernise Resources/ queries
2 parents 618a35b + aeaaab6 commit 0627fad

File tree

2 files changed

+53
-52
lines changed

2 files changed

+53
-52
lines changed

python/ql/src/Resources/FileNotAlwaysClosed.ql

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,42 @@
1515
import python
1616
import FileOpen
1717

18-
/** Whether resource is opened and closed in in a matched pair of methods,
19-
* either __enter__ and __exit__ or __init__ and __del__ */
18+
/**
19+
* Whether resource is opened and closed in in a matched pair of methods,
20+
* either `__enter__` and `__exit__` or `__init__` and `__del__`
21+
*/
2022
predicate opened_in_enter_closed_in_exit(ControlFlowNode open) {
2123
file_not_closed_at_scope_exit(open) and
22-
exists(FunctionObject entry, FunctionObject exit |
23-
open.getScope() = entry.getFunction() and
24-
exists(ClassObject cls |
24+
exists(FunctionValue entry, FunctionValue exit |
25+
open.getScope() = entry.getScope() and
26+
exists(ClassValue cls |
2527
cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit
2628
or
2729
cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit
28-
)
29-
and
30+
) and
3031
exists(AttrNode attr_open, AttrNode attrclose |
31-
attr_open.getScope() = entry.getFunction() and
32-
attrclose.getScope() = exit.getFunction() and
32+
attr_open.getScope() = entry.getScope() and
33+
attrclose.getScope() = exit.getScope() and
3334
expr_is_open(attr_open.(DefinitionNode).getValue(), open) and
3435
attr_open.getName() = attrclose.getName() and
3536
close_method_call(_, attrclose)
3637
)
3738
)
3839
}
3940

40-
predicate file_not_closed_at_scope_exit(ControlFlowNode open) {
41+
predicate file_not_closed_at_scope_exit(ControlFlowNode open) {
4142
exists(EssaVariable v |
4243
BaseFlow::reaches_exit(v) and
4344
var_is_open(v, open) and
4445
not file_is_returned(v, open)
4546
)
4647
or
47-
call_to_open(open) and not exists(AssignmentDefinition def | def.getValue() = open)
48-
and not exists(Return r | r.getValue() = open.getNode())
48+
call_to_open(open) and
49+
not exists(AssignmentDefinition def | def.getValue() = open) and
50+
not exists(Return r | r.getValue() = open.getNode())
4951
}
5052

51-
predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNode exit) {
53+
predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNode exit) {
5254
exists(EssaVariable v |
5355
exit.(RaisingNode).viableExceptionalExit(_, _) and
5456
not closes_arg(exit, v.getSourceVariable()) and
@@ -59,14 +61,14 @@ predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNod
5961
}
6062

6163
/* Check to see if a file is opened but not closed or returned */
62-
6364
from ControlFlowNode defn, string message
6465
where
65-
not opened_in_enter_closed_in_exit(defn) and
66-
(
67-
file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed."
68-
or
69-
not file_not_closed_at_scope_exit(defn) and file_not_closed_at_exception_exit(defn, _) and message = "File may not be closed if an exception is raised."
70-
)
71-
66+
not opened_in_enter_closed_in_exit(defn) and
67+
(
68+
file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed."
69+
or
70+
not file_not_closed_at_scope_exit(defn) and
71+
file_not_closed_at_exception_exit(defn, _) and
72+
message = "File may not be closed if an exception is raised."
73+
)
7274
select defn.getNode(), message

python/ql/src/Resources/FileOpen.qll

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import semmle.python.pointsto.Filters
44

55
/** Holds if `open` is a call that returns a newly opened file */
66
predicate call_to_open(ControlFlowNode open) {
7-
exists(FunctionObject f |
7+
exists(FunctionValue f |
88
function_opens_file(f) and
99
f.getACall() = open
1010
) and
@@ -18,7 +18,8 @@ predicate expr_is_open(ControlFlowNode n, ControlFlowNode open) {
1818
or
1919
exists(EssaVariable v |
2020
n instanceof NameNode and
21-
var_is_open(v, open) |
21+
var_is_open(v, open)
22+
|
2223
n = v.getAUse()
2324
or
2425
wraps_file(n, v)
@@ -27,7 +28,7 @@ predicate expr_is_open(ControlFlowNode n, ControlFlowNode open) {
2728

2829
/** Holds if `call` wraps the object referred to by `v` and returns it */
2930
private predicate wraps_file(CallNode call, EssaVariable v) {
30-
exists(ClassObject cls |
31+
exists(ClassValue cls |
3132
call = cls.getACall() and
3233
call.getAnArg() = v.getAUse()
3334
)
@@ -46,7 +47,8 @@ predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) {
4647
exists(AttrNode closed |
4748
closed = test and
4849
closed.getObject("closed") = v.getAUse()
49-
) and sense = false
50+
) and
51+
sense = false
5052
or
5153
// `if fd ==/is ...:` most commonly `if fd is None:`
5254
equality_test(test, v.getAUse(), sense.booleanNot(), _)
@@ -56,29 +58,30 @@ predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) {
5658
or
5759
exists(UnaryExprNode n |
5860
n = test and
59-
n.getNode().getOp() instanceof Not |
61+
n.getNode().getOp() instanceof Not
62+
|
6063
passes_open_files(v, n.getOperand(), sense.booleanNot())
6164
)
6265
}
6366

6467
/* Helper for `def_is_open` to give better join order */
6568
private predicate passes_open_files(PyEdgeRefinement refinement) {
66-
passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), refinement.getSense())
69+
passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(),
70+
refinement.getSense())
6771
}
6872

6973
/** Holds if `def` refers to a file opened at `open` */
7074
predicate def_is_open(EssaDefinition def, ControlFlowNode open) {
7175
expr_is_open(def.(AssignmentDefinition).getValue(), open)
7276
or
73-
exists(PyEdgeRefinement refinement |
74-
refinement = def |
75-
var_is_open(refinement.getInput(), open) and
77+
exists(PyEdgeRefinement refinement | refinement = def |
78+
var_is_open(refinement.getInput(), open) and
7679
passes_open_files(refinement)
7780
)
7881
or
79-
exists(PyNodeRefinement refinement |
80-
refinement = def |
81-
not closes_file(def) and not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and
82+
exists(PyNodeRefinement refinement | refinement = def |
83+
not closes_file(def) and
84+
not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and
8285
var_is_open(refinement.getInput(), open)
8386
)
8487
or
@@ -88,16 +91,15 @@ predicate def_is_open(EssaDefinition def, ControlFlowNode open) {
8891
/** Holds if `call` closes a file */
8992
predicate closes_file(EssaNodeRefinement call) {
9093
closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or
91-
close_method_call(call.(MethodCallsiteRefinement).getCall(), call.getSourceVariable().(Variable).getAUse())
94+
close_method_call(call.(MethodCallsiteRefinement).getCall(),
95+
call.getSourceVariable().(Variable).getAUse())
9296
}
9397

9498
/** Holds if `call` closes its argument, which is an open file referred to by `v` */
9599
predicate closes_arg(CallNode call, Variable v) {
96100
call.getAnArg() = v.getAUse() and
97101
(
98-
exists(FunctionObject close |
99-
call = close.getACall() and function_closes_file(close)
100-
)
102+
exists(FunctionValue close | call = close.getACall() and function_closes_file(close))
101103
or
102104
call.getFunction().(NameNode).getId() = "close"
103105
)
@@ -106,17 +108,15 @@ predicate closes_arg(CallNode call, Variable v) {
106108
/** Holds if `call` closes its 'self' argument, which is an open file referred to by `v` */
107109
predicate close_method_call(CallNode call, ControlFlowNode self) {
108110
call.getFunction().(AttrNode).getObject() = self and
109-
exists(FunctionObject close |
110-
call = close.getACall() and function_closes_file(close)
111-
)
111+
exists(FunctionValue close | call = close.getACall() and function_closes_file(close))
112112
or
113113
call.getFunction().(AttrNode).getObject("close") = self
114114
}
115115

116-
predicate function_closes_file(FunctionObject close) {
117-
close.hasLongName("os.close")
116+
predicate function_closes_file(FunctionValue close) {
117+
close = Value::named("os.close")
118118
or
119-
function_should_close_parameter(close.getFunction())
119+
function_should_close_parameter(close.getScope())
120120
}
121121

122122
predicate function_should_close_parameter(Function func) {
@@ -126,26 +126,25 @@ predicate function_should_close_parameter(Function func) {
126126
)
127127
}
128128

129-
predicate function_opens_file(FunctionObject f) {
130-
f = Object::builtin("open")
129+
predicate function_opens_file(FunctionValue f) {
130+
f = Value::named("open")
131131
or
132-
exists(EssaVariable v, Return ret |
133-
ret.getScope() = f.getFunction() |
134-
ret.getValue().getAFlowNode() = v.getAUse() and
132+
exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() |
133+
ret.getValue().getAFlowNode() = v.getAUse() and
135134
var_is_open(v, _)
136135
)
137136
or
138-
exists(Return ret, FunctionObject callee |
139-
ret.getScope() = f.getFunction() |
140-
ret.getValue().getAFlowNode() = callee.getACall() and
137+
exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() |
138+
ret.getValue().getAFlowNode() = callee.getACall() and
141139
function_opens_file(callee)
142140
)
143141
}
144142

145143
predicate file_is_returned(EssaVariable v, ControlFlowNode open) {
146144
exists(NameNode n, Return ret |
147145
var_is_open(v, open) and
148-
v.getAUse() = n |
146+
v.getAUse() = n
147+
|
149148
ret.getValue() = n.getNode()
150149
or
151150
ret.getValue().(Tuple).getAnElt() = n.getNode()

0 commit comments

Comments
 (0)