@@ -101,44 +101,6 @@ predicate postDominatesNode(ControlFlow::Node postDominator, ControlFlow::Node n
101101 )
102102}
103103
104- /**
105- * Holds if `closeCall` is an unhandled call to `os.File.Close` on `sink` that is not
106- * guaranteed to be preceded during execution by a handled call to `os.File.Sync` on the
107- * same file handle.
108- */
109- predicate isCloseSink ( DataFlow:: Node sink , DataFlow:: CallNode closeCall ) {
110- // find calls to the os.File.Close function
111- closeCall = any ( CloseFileFun f ) .getACall ( ) and
112- // that are unhandled
113- unhandledCall ( closeCall ) and
114- // where the function is called on the sink
115- closeCall .getReceiver ( ) = sink and
116- // and check that the call to `os.File.Close` is not guaranteed to be preceded during
117- // execution by a handled call to `os.File.Sync` on the same file handle.
118- not exists ( DataFlow:: Node syncReceiver , DataFlow:: CallNode syncCall |
119- // check that the call to `os.File.Sync` is handled
120- isHandledSync ( syncReceiver , syncCall ) and
121- // check that `os.File.Sync` is called on the same object as `os.File.Close`
122- exists ( DataFlow:: SsaNode ssa | ssa .getAUse ( ) = sink and ssa .getAUse ( ) = syncReceiver )
123- |
124- if exists ( DeferStmt defer | defer .getCall ( ) = closeCall .asExpr ( ) )
125- then
126- // When the call to `os.File.Close` is deferred it runs when the enclosing function
127- // returns, but the receiver of the deferred call is evaluated where the `defer`
128- // statement appears. It is therefore enough for the handled call to `os.File.Sync`
129- // to post-dominate that point, since that guarantees `os.File.Sync` runs before the
130- // deferred `os.File.Close` on every path on which the `os.File.Close` is registered.
131- // We cannot reuse the domination check below because the control-flow graph splices
132- // the deferred call in at the function exit, where it may be reachable along paths
133- // that do not pass through the call to `os.File.Sync`.
134- postDominatesNode ( syncCall .asInstruction ( ) , sink .asInstruction ( ) )
135- else
136- // Otherwise the call to `os.File.Close` is executed where it appears, so we require
137- // the handled call to `os.File.Sync` to dominate it.
138- syncCall .asInstruction ( ) .dominatesNode ( closeCall .asInstruction ( ) )
139- )
140- }
141-
142104/**
143105 * Holds if `os.File.Sync` is called on `sink` and the result of the call is neither
144106 * deferred nor discarded.
@@ -155,7 +117,43 @@ predicate isHandledSync(DataFlow::Node sink, DataFlow::CallNode syncCall) {
155117module UnhandledFileCloseConfig implements DataFlow:: ConfigSig {
156118 predicate isSource ( DataFlow:: Node source ) { isWritableFileHandle ( source , _) }
157119
158- predicate isSink ( DataFlow:: Node sink ) { isCloseSink ( sink , _) }
120+ predicate isSink ( DataFlow:: Node sink ) {
121+ // `closeCall` is an unhandled call to `os.File.Close` on `sink` that is not
122+ // guaranteed to be preceded during execution by a handled call to `os.File.Sync` on the
123+ // same file handle.
124+ exists ( DataFlow:: CallNode closeCall |
125+ // find calls to the os.File.Close function
126+ closeCall = any ( CloseFileFun f ) .getACall ( ) and
127+ // that are unhandled
128+ unhandledCall ( closeCall ) and
129+ // where the function is called on the sink
130+ closeCall .getReceiver ( ) = sink and
131+ // and check that the call to `os.File.Close` is not guaranteed to be preceded during
132+ // execution by a handled call to `os.File.Sync` on the same file handle.
133+ not exists ( DataFlow:: Node syncReceiver , DataFlow:: CallNode syncCall |
134+ // check that the call to `os.File.Sync` is handled
135+ isHandledSync ( syncReceiver , syncCall ) and
136+ // check that `os.File.Sync` is called on the same object as `os.File.Close`
137+ exists ( DataFlow:: SsaNode ssa | ssa .getAUse ( ) = sink and ssa .getAUse ( ) = syncReceiver )
138+ |
139+ if exists ( DeferStmt defer | defer .getCall ( ) = closeCall .asExpr ( ) )
140+ then
141+ // When the call to `os.File.Close` is deferred it runs when the enclosing function
142+ // returns, but the receiver of the deferred call is evaluated where the `defer`
143+ // statement appears. It is therefore enough for the handled call to `os.File.Sync`
144+ // to post-dominate that point, since that guarantees `os.File.Sync` runs before the
145+ // deferred `os.File.Close` on every path on which the `os.File.Close` is registered.
146+ // We cannot reuse the domination check below because the control-flow graph splices
147+ // the deferred call in at the function exit, where it may be reachable along paths
148+ // that do not pass through the call to `os.File.Sync`.
149+ postDominatesNode ( syncCall .asInstruction ( ) , sink .asInstruction ( ) )
150+ else
151+ // Otherwise the call to `os.File.Close` is executed where it appears, so we require
152+ // the handled call to `os.File.Sync` to dominate it.
153+ syncCall .asInstruction ( ) .dominatesNode ( closeCall .asInstruction ( ) )
154+ )
155+ )
156+ }
159157
160158 predicate observeDiffInformedIncrementalMode ( ) { any ( ) }
161159
0 commit comments