Skip to content

Commit 1018123

Browse files
committed
Inline isCloseCall into isSink
1 parent c87bfd5 commit 1018123

1 file changed

Lines changed: 37 additions & 39 deletions

File tree

go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {
155117
module 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

Comments
 (0)