Skip to content

Commit 71daa20

Browse files
authored
Merge branch 'main' into add-yaml-comments
2 parents c12cf88 + 6161922 commit 71daa20

7 files changed

Lines changed: 84 additions & 41 deletions

File tree

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @github/code-scanning-alert-coverage
33

44
# CodeQL language libraries
5-
/actions/ @github/codeql-dynamic
5+
/actions/ @github/code-scanning-alert-coverage
66
/cpp/ @github/codeql-c-analysis
77
/csharp/ @github/codeql-csharp
88
/csharp/autobuilder/Semmle.Autobuild.Cpp @github/codeql-c-extractor @github/code-scanning-language-coverage

go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class SyncFileFun extends Method {
5555

5656
/**
5757
* Holds if a `call` to a function is "unhandled". That is, it is either
58-
* deferred or its result is not assigned to anything.
58+
* deferred or used as an expression statement, so that its result is discarded.
5959
*
6060
* TODO: maybe we should check that something is actually done with the result
6161
*/
@@ -77,7 +77,6 @@ predicate isWritableFileHandle(DataFlow::Node source, DataFlow::CallNode call) {
7777
// get the flags expression used for opening the file
7878
call.getArgument(1) = flags and
7979
// extract individual flags from the argument
80-
// flag = flag.getAChild*() and
8180
flag = getConstants(flags.asExpr()) and
8281
// check for one which signals that the handle will be writable
8382
// note that we are underestimating here, since the flags may be
@@ -87,27 +86,18 @@ predicate isWritableFileHandle(DataFlow::Node source, DataFlow::CallNode call) {
8786
}
8887

8988
/**
90-
* Holds if `os.File.Close` is called on `sink`.
89+
* Holds if `postDominator` post-dominates `node` in the control-flow graph. That is,
90+
* every path from `node` to the exit of the enclosing function passes through
91+
* `postDominator`.
9192
*/
92-
predicate isCloseSink(DataFlow::Node sink, DataFlow::CallNode closeCall) {
93-
// find calls to the os.File.Close function
94-
closeCall = any(CloseFileFun f).getACall() and
95-
// that are unhandled
96-
unhandledCall(closeCall) and
97-
// where the function is called on the sink
98-
closeCall.getReceiver() = sink and
99-
// and check that it is not dominated by a call to `os.File.Sync`.
100-
// TODO: fix this logic when `closeCall` is in a defer statement.
101-
not exists(IR::Instruction syncInstr, DataFlow::Node syncReceiver, DataFlow::CallNode syncCall |
102-
// match the instruction corresponding to an `os.File.Sync` call with the predecessor
103-
syncCall.asInstruction() = syncInstr and
104-
// check that the call to `os.File.Sync` is handled
105-
isHandledSync(syncReceiver, syncCall) and
106-
// find a predecessor to `closeCall` in the control flow graph which dominates the call to
107-
// `os.File.Close`
108-
syncInstr.dominatesNode(closeCall.asInstruction()) and
109-
// check that `os.File.Sync` is called on the same object as `os.File.Close`
110-
exists(DataFlow::SsaNode ssa | ssa.getAUse() = sink and ssa.getAUse() = syncReceiver)
93+
pragma[inline]
94+
predicate postDominatesNode(ControlFlow::Node postDominator, ControlFlow::Node node) {
95+
exists(ReachableBasicBlock pdbb, ReachableBasicBlock nbb, int i, int j |
96+
postDominator = pdbb.getNode(i) and node = nbb.getNode(j)
97+
|
98+
pdbb.strictlyPostDominates(nbb)
99+
or
100+
pdbb = nbb and i >= j
111101
)
112102
}
113103

@@ -127,7 +117,39 @@ predicate isHandledSync(DataFlow::Node sink, DataFlow::CallNode syncCall) {
127117
module UnhandledFileCloseConfig implements DataFlow::ConfigSig {
128118
predicate isSource(DataFlow::Node source) { isWritableFileHandle(source, _) }
129119

130-
predicate isSink(DataFlow::Node sink) { isCloseSink(sink, _) }
120+
predicate isSink(DataFlow::Node sink) {
121+
exists(DataFlow::CallNode closeCall |
122+
// `closeCall` is an unhandled call to `os.File.Close` on `sink`
123+
closeCall = any(CloseFileFun f).getACall() and
124+
unhandledCall(closeCall) and
125+
closeCall.getReceiver() = sink
126+
|
127+
// `closeCall` is not guaranteed to be preceded during
128+
// execution by a handled call to `os.File.Sync` on the same file handle.
129+
not exists(DataFlow::Node syncReceiver, DataFlow::CallNode syncCall |
130+
// check that the call to `os.File.Sync` is handled
131+
isHandledSync(syncReceiver, syncCall) and
132+
// check that `os.File.Sync` is called on the same object as `os.File.Close`
133+
exists(DataFlow::SsaNode ssa | ssa.getAUse() = sink and ssa.getAUse() = syncReceiver)
134+
|
135+
if exists(DeferStmt defer | defer.getCall() = closeCall.asExpr())
136+
then
137+
// When the call to `os.File.Close` is deferred it runs when the enclosing function
138+
// returns, but the receiver of the deferred call is evaluated where the `defer`
139+
// statement appears. It is therefore enough for the handled call to `os.File.Sync`
140+
// to post-dominate that point, since that guarantees `os.File.Sync` runs before the
141+
// deferred `os.File.Close` on every path on which the `os.File.Close` is registered.
142+
// We cannot reuse the domination check below because the control-flow graph splices
143+
// the deferred call in at the function exit, where it may be reachable along paths
144+
// that do not pass through the call to `os.File.Sync`.
145+
postDominatesNode(syncCall.asInstruction(), sink.asInstruction())
146+
else
147+
// Otherwise the call to `os.File.Close` is executed where it appears, so we require
148+
// the handled call to `os.File.Sync` to dominate it.
149+
syncCall.asInstruction().dominatesNode(closeCall.asInstruction())
150+
)
151+
)
152+
}
131153

132154
predicate observeDiffInformedIncrementalMode() { any() }
133155

@@ -148,14 +170,12 @@ import UnhandledFileCloseFlow::PathGraph
148170

149171
from
150172
UnhandledFileCloseFlow::PathNode source, DataFlow::CallNode openCall,
151-
UnhandledFileCloseFlow::PathNode sink, DataFlow::CallNode closeCall
173+
UnhandledFileCloseFlow::PathNode sink
152174
where
153175
// find data flow from an `os.OpenFile` call to an `os.File.Close` call
154176
// where the handle is writable
155177
UnhandledFileCloseFlow::flowPath(source, sink) and
156-
isWritableFileHandle(source.getNode(), openCall) and
157-
// get the `CallNode` corresponding to the sink
158-
isCloseSink(sink.getNode(), closeCall)
178+
isWritableFileHandle(source.getNode(), openCall)
159179
select sink, source, sink,
160180
"File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly.",
161181
openCall, openCall.toString()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The query `go/unhandled-writable-file-close` ("Writable file handle closed without error handling") now produces fewer false positives. A deferred call to `Close` that is preceded on every execution path by a handled call to `Sync` on the same file handle is no longer flagged.

go/ql/test/query-tests/InconsistentCode/UnhandledCloseWritableHandle/UnhandledCloseWritableHandle.expected

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
| tests.go:15:3:15:3 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile |
66
| tests.go:57:3:57:3 | f | tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:55:15:55:78 | call to OpenFile | call to OpenFile |
77
| tests.go:69:3:69:3 | f | tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:67:15:67:76 | call to OpenFile | call to OpenFile |
8-
| tests.go:111:9:111:9 | f | tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:109:15:109:78 | call to OpenFile | call to OpenFile |
9-
| tests.go:130:3:130:3 | f | tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:126:15:126:78 | call to OpenFile | call to OpenFile |
10-
| tests.go:151:8:151:8 | f | tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:147:12:147:74 | call to OpenFile | call to OpenFile |
8+
| tests.go:126:9:126:9 | f | tests.go:124:5:124:78 | ... := ...[0] | tests.go:126:9:126:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:124:15:124:78 | call to OpenFile | call to OpenFile |
9+
| tests.go:145:3:145:3 | f | tests.go:141:5:141:78 | ... := ...[0] | tests.go:145:3:145:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:141:15:141:78 | call to OpenFile | call to OpenFile |
10+
| tests.go:166:8:166:8 | f | tests.go:162:2:162:74 | ... := ...[0] | tests.go:166:8:166:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:162:12:162:74 | call to OpenFile | call to OpenFile |
1111
edges
1212
| tests.go:9:24:9:24 | definition of f | tests.go:10:8:10:8 | f | provenance | |
1313
| tests.go:13:32:13:32 | definition of f | tests.go:14:13:16:2 | capture variable f | provenance | |
@@ -22,9 +22,9 @@ edges
2222
| tests.go:48:29:48:29 | f | tests.go:13:32:13:32 | definition of f | provenance | |
2323
| tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 |
2424
| tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | provenance | Src:MaD:1 |
25-
| tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | provenance | Src:MaD:1 |
26-
| tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | provenance | Src:MaD:1 |
27-
| tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | provenance | Src:MaD:1 |
25+
| tests.go:124:5:124:78 | ... := ...[0] | tests.go:126:9:126:9 | f | provenance | Src:MaD:1 |
26+
| tests.go:141:5:141:78 | ... := ...[0] | tests.go:145:3:145:3 | f | provenance | Src:MaD:1 |
27+
| tests.go:162:2:162:74 | ... := ...[0] | tests.go:166:8:166:8 | f | provenance | Src:MaD:1 |
2828
models
2929
| 1 | Source: os; ; false; OpenFile; ; ; ReturnValue[0]; file; manual |
3030
nodes
@@ -43,10 +43,10 @@ nodes
4343
| tests.go:57:3:57:3 | f | semmle.label | f |
4444
| tests.go:67:5:67:76 | ... := ...[0] | semmle.label | ... := ...[0] |
4545
| tests.go:69:3:69:3 | f | semmle.label | f |
46-
| tests.go:109:5:109:78 | ... := ...[0] | semmle.label | ... := ...[0] |
47-
| tests.go:111:9:111:9 | f | semmle.label | f |
48-
| tests.go:126:5:126:78 | ... := ...[0] | semmle.label | ... := ...[0] |
49-
| tests.go:130:3:130:3 | f | semmle.label | f |
50-
| tests.go:147:2:147:74 | ... := ...[0] | semmle.label | ... := ...[0] |
51-
| tests.go:151:8:151:8 | f | semmle.label | f |
46+
| tests.go:124:5:124:78 | ... := ...[0] | semmle.label | ... := ...[0] |
47+
| tests.go:126:9:126:9 | f | semmle.label | f |
48+
| tests.go:141:5:141:78 | ... := ...[0] | semmle.label | ... := ...[0] |
49+
| tests.go:145:3:145:3 | f | semmle.label | f |
50+
| tests.go:162:2:162:74 | ... := ...[0] | semmle.label | ... := ...[0] |
51+
| tests.go:166:8:166:8 | f | semmle.label | f |
5252
subpaths

go/ql/test/query-tests/InconsistentCode/UnhandledCloseWritableHandle/tests.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@ func deferredCloseWithSync() {
104104
}
105105
}
106106

107+
func deferredCloseWithSync2() {
108+
// open file for writing
109+
if f, err := os.OpenFile("foo.txt", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666); err != nil {
110+
// a call to `Close` is deferred, but we have a call to `Sync` later which
111+
// precedes the call to `Close` during execution
112+
defer f.Close()
113+
114+
if err := f.Sync(); err != nil {
115+
log.Fatal(err)
116+
}
117+
}
118+
var a int
119+
_ = a
120+
}
121+
107122
func deferredCloseWithSyncEarlyReturn(n int) {
108123
// open file for writing
109124
if f, err := os.OpenFile("foo.txt", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666); err != nil { // $ Source
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import swift
22

33
from File f
4-
where exists(f.getRelativePath()) or f instanceof UnknownFile
4+
where
5+
(exists(f.getRelativePath()) or f instanceof UnknownFile) and
6+
not f.getBaseName() = "<module-includes>"
57
select f

swift/tools/tracing-config.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ function RegisterExtractorPack(id)
5454
strip_unsupported_arg(args, '-experimental-skip-non-inlinable-function-bodies-without-types', 0)
5555
strip_unsupported_clang_arg(args, '-ivfsstatcache', 1)
5656
strip_unsupported_clang_arg(args, '-fno-odr-hash-protocols', 0)
57+
strip_unsupported_clang_arg(args, '-fobjc-msgsend-selector-stubs', 0)
58+
strip_unsupported_clang_arg(args, '-fstack-check', 0)
5759
strip_unsupported_clang_arg(args, '-clang-vendor-feature=+disableNonDependentMemberExprInCurrentInstantiation', 0)
5860
strip_unsupported_clang_arg(args, '-clang-vendor-feature=+enableAggressiveVLAFolding', 0)
5961
strip_unsupported_clang_arg(args, '-clang-vendor-feature=+revert09abecef7bbf', 0)

0 commit comments

Comments
 (0)