@@ -21,14 +21,17 @@ private DataFlow::TypeTrackingNode fileOpenInstance(DataFlow::TypeTracker t) {
21
21
exists ( DataFlow:: TypeTracker t2 | result = fileOpenInstance ( t2 ) .track ( t2 , t ) )
22
22
}
23
23
24
- /** A call that returns an instance of an open file object. */
24
+ /**
25
+ * A call that returns an instance of an open file object.
26
+ * This includes calls to methods that transitively call `open` or similar.
27
+ */
25
28
class FileOpen extends DataFlow:: CallCfgNode {
26
29
FileOpen ( ) { fileOpenInstance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( this ) }
27
30
}
28
31
29
32
/** A call that may wrap a file object in a wrapper class or `os.fdopen`. */
30
33
class FileWrapperCall extends DataFlow:: CallCfgNode {
31
- FileOpen wrapped ;
34
+ DataFlow :: Node wrapped ;
32
35
33
36
FileWrapperCall ( ) {
34
37
wrapped = this .getArg ( _) .getALocalSource ( ) and
@@ -42,18 +45,18 @@ class FileWrapperCall extends DataFlow::CallCfgNode {
42
45
}
43
46
44
47
/** Gets the file that this call wraps. */
45
- FileOpen getWrapped ( ) { result = wrapped }
48
+ DataFlow :: Node getWrapped ( ) { result = wrapped }
46
49
}
47
50
48
51
/** A node where a file is closed. */
49
52
abstract class FileClose extends DataFlow:: CfgNode {
50
53
/** Holds if this file close will occur if an exception is thrown at `e`. */
51
- predicate guardsExceptions ( Expr e ) {
52
- this .asCfgNode ( ) =
53
- DataFlow:: exprNode ( e ) .asCfgNode ( ) .getAnExceptionalSuccessor ( ) .getASuccessor * ( )
54
+ predicate guardsExceptions ( DataFlow:: CfgNode raises ) {
55
+ this .asCfgNode ( ) = raises .asCfgNode ( ) .getAnExceptionalSuccessor ( ) .getASuccessor * ( )
54
56
or
55
- // the expression is after the close call
56
- DataFlow:: exprNode ( e ) .asCfgNode ( ) = this .asCfgNode ( ) .getASuccessor * ( )
57
+ // The expression is after the close call.
58
+ // This also covers the body of a `with` statement.
59
+ raises .asCfgNode ( ) = this .asCfgNode ( ) .getASuccessor * ( )
57
60
}
58
61
}
59
62
@@ -72,20 +75,14 @@ class WithStatement extends FileClose {
72
75
With w ;
73
76
74
77
WithStatement ( ) { this .asExpr ( ) = w .getContextExpr ( ) }
75
-
76
- override predicate guardsExceptions ( Expr e ) {
77
- super .guardsExceptions ( e )
78
- or
79
- e = w .getAStmt ( ) .getAChildNode * ( )
80
- }
81
78
}
82
79
83
- /** Holds if an exception may be raised at `node ` if it is a file object. */
84
- private predicate mayRaiseWithFile ( DataFlow:: CfgNode node ) {
80
+ /** Holds if an exception may be raised at `raises ` if `file` is a file object. */
81
+ private predicate mayRaiseWithFile ( DataFlow:: CfgNode file , DataFlow :: CfgNode raises ) {
85
82
// Currently just consider any method called on `node`; e.g. `file.write()`; as potentially raising an exception
86
- exists ( DataFlow:: MethodCallNode mc | node = mc .getObject ( ) ) and
87
- not node instanceof FileOpen and
88
- not node instanceof FileClose
83
+ raises . ( DataFlow:: MethodCallNode ) .getObject ( ) = file and
84
+ not file instanceof FileOpen and
85
+ not file instanceof FileClose
89
86
}
90
87
91
88
/** Holds if data flows from `nodeFrom` to `nodeTo` in one step that also includes file wrapper classes. */
@@ -131,10 +128,12 @@ predicate fileNotClosed(FileOpen fo) {
131
128
132
129
predicate fileMayNotBeClosedOnException ( FileOpen fo , DataFlow:: Node raises ) {
133
130
fileIsClosed ( fo ) and
134
- mayRaiseWithFile ( raises ) and
135
- fileLocalFlow ( fo , raises ) and
136
- not exists ( FileClose fc |
137
- DataFlow:: localFlow ( fo , fc ) and
138
- fc .guardsExceptions ( raises .asExpr ( ) )
131
+ exists ( DataFlow:: CfgNode fileRaised |
132
+ mayRaiseWithFile ( fileRaised , raises ) and
133
+ fileLocalFlow ( fo , fileRaised ) and
134
+ not exists ( FileClose fc |
135
+ fileLocalFlow ( fo , fc ) and
136
+ fc .guardsExceptions ( raises )
137
+ )
139
138
)
140
139
}
0 commit comments