@@ -21,14 +21,17 @@ private DataFlow::TypeTrackingNode fileOpenInstance(DataFlow::TypeTracker t) {
2121 exists ( DataFlow:: TypeTracker t2 | result = fileOpenInstance ( t2 ) .track ( t2 , t ) )
2222}
2323
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+ */
2528class FileOpen extends DataFlow:: CallCfgNode {
2629 FileOpen ( ) { fileOpenInstance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( this ) }
2730}
2831
2932/** A call that may wrap a file object in a wrapper class or `os.fdopen`. */
3033class FileWrapperCall extends DataFlow:: CallCfgNode {
31- FileOpen wrapped ;
34+ DataFlow :: Node wrapped ;
3235
3336 FileWrapperCall ( ) {
3437 wrapped = this .getArg ( _) .getALocalSource ( ) and
@@ -42,18 +45,18 @@ class FileWrapperCall extends DataFlow::CallCfgNode {
4245 }
4346
4447 /** Gets the file that this call wraps. */
45- FileOpen getWrapped ( ) { result = wrapped }
48+ DataFlow :: Node getWrapped ( ) { result = wrapped }
4649}
4750
4851/** A node where a file is closed. */
4952abstract class FileClose extends DataFlow:: CfgNode {
5053 /** 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 * ( )
5456 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 * ( )
5760 }
5861}
5962
@@ -72,20 +75,14 @@ class WithStatement extends FileClose {
7275 With w ;
7376
7477 WithStatement ( ) { this .asExpr ( ) = w .getContextExpr ( ) }
75-
76- override predicate guardsExceptions ( Expr e ) {
77- super .guardsExceptions ( e )
78- or
79- e = w .getAStmt ( ) .getAChildNode * ( )
80- }
8178}
8279
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 ) {
8582 // 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
8986}
9087
9188/** 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) {
131128
132129predicate fileMayNotBeClosedOnException ( FileOpen fo , DataFlow:: Node raises ) {
133130 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+ )
139138 )
140139}
0 commit comments