Skip to content

Commit 7b29123

Browse files
committed
Add failing test for os.File.Sync with defered Close calls
1 parent 63bfa36 commit 7b29123

File tree

3 files changed

+74
-42
lines changed

3 files changed

+74
-42
lines changed

go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ predicate isCloseSink(DataFlow::Node sink, DataFlow::CallNode closeCall) {
9797
// where the function is called on the sink
9898
closeCall.getReceiver() = sink and
9999
// 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.
100101
not exists(IR::Instruction syncInstr, DataFlow::Node syncReceiver, DataFlow::CallNode syncCall |
101102
// match the instruction corresponding to an `os.File.Sync` call with the predecessor
102103
syncCall.asInstruction() = syncInstr and
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,52 @@
11
#select
2-
| tests.go:9:8:9:8 | f | tests.go:31:5:31:78 | ... := ...[0] | tests.go:9:8:9: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:31:15:31:78 | call to OpenFile | call to OpenFile |
3-
| tests.go:9:8:9:8 | f | tests.go:45:5:45:76 | ... := ...[0] | tests.go:9:8:9: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:45:15:45:76 | call to OpenFile | call to OpenFile |
4-
| tests.go:14:3:14:3 | f | tests.go:31:5:31:78 | ... := ...[0] | tests.go:14:3:14: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:31:15:31:78 | call to OpenFile | call to OpenFile |
5-
| tests.go:14:3:14:3 | f | tests.go:45:5:45:76 | ... := ...[0] | tests.go:14:3:14: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:45:15:45:76 | call to OpenFile | call to OpenFile |
6-
| tests.go:56:3:56:3 | f | tests.go:54:5:54:78 | ... := ...[0] | tests.go:56:3:56: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:54:15:54:78 | call to OpenFile | call to OpenFile |
7-
| tests.go:68:3:68:3 | f | tests.go:66:5:66:76 | ... := ...[0] | tests.go:68:3:68: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:66:15:66:76 | call to OpenFile | call to OpenFile |
8-
| tests.go:110:9:110:9 | f | tests.go:108:5:108:78 | ... := ...[0] | tests.go:110:9:110: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:108:15:108:78 | call to OpenFile | call to OpenFile |
9-
| tests.go:129:3:129:3 | f | tests.go:125:5:125:78 | ... := ...[0] | tests.go:129:3:129: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:125:15:125:78 | call to OpenFile | call to OpenFile |
2+
| tests.go:10:8:10:8 | f | tests.go:32:5:32:78 | ... := ...[0] | tests.go:10:8:10: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:32:15:32:78 | call to OpenFile | call to OpenFile |
3+
| tests.go:10:8:10:8 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:10:8:10: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:46:15:46:76 | call to OpenFile | call to OpenFile |
4+
| tests.go:15:3:15:3 | f | tests.go:32:5:32:78 | ... := ...[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:32:15:32:78 | call to OpenFile | call to OpenFile |
5+
| 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 |
6+
| 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 |
7+
| 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 |
1011
edges
11-
| tests.go:8:24:8:24 | definition of f | tests.go:9:8:9:8 | f | provenance | |
12-
| tests.go:12:32:12:32 | definition of f | tests.go:13:13:15:2 | capture variable f | provenance | |
13-
| tests.go:13:13:15:2 | capture variable f | tests.go:14:3:14:3 | f | provenance | |
14-
| tests.go:31:5:31:78 | ... := ...[0] | tests.go:32:21:32:21 | f | provenance | Src:MaD:1 |
15-
| tests.go:31:5:31:78 | ... := ...[0] | tests.go:33:29:33:29 | f | provenance | Src:MaD:1 |
16-
| tests.go:32:21:32:21 | f | tests.go:8:24:8:24 | definition of f | provenance | |
17-
| tests.go:33:29:33:29 | f | tests.go:12:32:12:32 | definition of f | provenance | |
18-
| tests.go:45:5:45:76 | ... := ...[0] | tests.go:46:21:46:21 | f | provenance | Src:MaD:1 |
19-
| tests.go:45:5:45:76 | ... := ...[0] | tests.go:47:29:47:29 | f | provenance | Src:MaD:1 |
20-
| tests.go:46:21:46:21 | f | tests.go:8:24:8:24 | definition of f | provenance | |
21-
| tests.go:47:29:47:29 | f | tests.go:12:32:12:32 | definition of f | provenance | |
22-
| tests.go:54:5:54:78 | ... := ...[0] | tests.go:56:3:56:3 | f | provenance | Src:MaD:1 |
23-
| tests.go:66:5:66:76 | ... := ...[0] | tests.go:68:3:68:3 | f | provenance | Src:MaD:1 |
24-
| tests.go:108:5:108:78 | ... := ...[0] | tests.go:110:9:110:9 | f | provenance | Src:MaD:1 |
25-
| tests.go:125:5:125:78 | ... := ...[0] | tests.go:129:3:129:3 | f | provenance | Src:MaD:1 |
12+
| tests.go:9:24:9:24 | definition of f | tests.go:10:8:10:8 | f | provenance | |
13+
| tests.go:13:32:13:32 | definition of f | tests.go:14:13:16:2 | capture variable f | provenance | |
14+
| tests.go:14:13:16:2 | capture variable f | tests.go:15:3:15:3 | f | provenance | |
15+
| tests.go:32:5:32:78 | ... := ...[0] | tests.go:33:21:33:21 | f | provenance | Src:MaD:1 |
16+
| tests.go:32:5:32:78 | ... := ...[0] | tests.go:34:29:34:29 | f | provenance | Src:MaD:1 |
17+
| tests.go:33:21:33:21 | f | tests.go:9:24:9:24 | definition of f | provenance | |
18+
| tests.go:34:29:34:29 | f | tests.go:13:32:13:32 | definition of f | provenance | |
19+
| tests.go:46:5:46:76 | ... := ...[0] | tests.go:47:21:47:21 | f | provenance | Src:MaD:1 |
20+
| tests.go:46:5:46:76 | ... := ...[0] | tests.go:48:29:48:29 | f | provenance | Src:MaD:1 |
21+
| tests.go:47:21:47:21 | f | tests.go:9:24:9:24 | definition of f | provenance | |
22+
| tests.go:48:29:48:29 | f | tests.go:13:32:13:32 | definition of f | provenance | |
23+
| tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 |
24+
| 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 |
2628
models
2729
| 1 | Source: os; ; false; OpenFile; ; ; ReturnValue[0]; file; manual |
2830
nodes
29-
| tests.go:8:24:8:24 | definition of f | semmle.label | definition of f |
30-
| tests.go:9:8:9:8 | f | semmle.label | f |
31-
| tests.go:12:32:12:32 | definition of f | semmle.label | definition of f |
32-
| tests.go:13:13:15:2 | capture variable f | semmle.label | capture variable f |
33-
| tests.go:14:3:14:3 | f | semmle.label | f |
34-
| tests.go:31:5:31:78 | ... := ...[0] | semmle.label | ... := ...[0] |
35-
| tests.go:32:21:32:21 | f | semmle.label | f |
36-
| tests.go:33:29:33:29 | f | semmle.label | f |
37-
| tests.go:45:5:45:76 | ... := ...[0] | semmle.label | ... := ...[0] |
38-
| tests.go:46:21:46:21 | f | semmle.label | f |
39-
| tests.go:47:29:47:29 | f | semmle.label | f |
40-
| tests.go:54:5:54:78 | ... := ...[0] | semmle.label | ... := ...[0] |
41-
| tests.go:56:3:56:3 | f | semmle.label | f |
42-
| tests.go:66:5:66:76 | ... := ...[0] | semmle.label | ... := ...[0] |
43-
| tests.go:68:3:68:3 | f | semmle.label | f |
44-
| tests.go:108:5:108:78 | ... := ...[0] | semmle.label | ... := ...[0] |
45-
| tests.go:110:9:110:9 | f | semmle.label | f |
46-
| tests.go:125:5:125:78 | ... := ...[0] | semmle.label | ... := ...[0] |
47-
| tests.go:129:3:129:3 | f | semmle.label | f |
31+
| tests.go:9:24:9:24 | definition of f | semmle.label | definition of f |
32+
| tests.go:10:8:10:8 | f | semmle.label | f |
33+
| tests.go:13:32:13:32 | definition of f | semmle.label | definition of f |
34+
| tests.go:14:13:16:2 | capture variable f | semmle.label | capture variable f |
35+
| tests.go:15:3:15:3 | f | semmle.label | f |
36+
| tests.go:32:5:32:78 | ... := ...[0] | semmle.label | ... := ...[0] |
37+
| tests.go:33:21:33:21 | f | semmle.label | f |
38+
| tests.go:34:29:34:29 | f | semmle.label | f |
39+
| tests.go:46:5:46:76 | ... := ...[0] | semmle.label | ... := ...[0] |
40+
| tests.go:47:21:47:21 | f | semmle.label | f |
41+
| tests.go:48:29:48:29 | f | semmle.label | f |
42+
| tests.go:55:5:55:78 | ... := ...[0] | semmle.label | ... := ...[0] |
43+
| tests.go:57:3:57:3 | f | semmle.label | f |
44+
| tests.go:67:5:67:76 | ... := ...[0] | semmle.label | ... := ...[0] |
45+
| 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 |
4852
subpaths

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

+27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package test
22

33
import (
4+
"io"
45
"log"
56
"os"
67
)
@@ -129,3 +130,29 @@ func unhandledSync() {
129130
f.Close() // $ Alert
130131
}
131132
}
133+
134+
func returnedSync() error {
135+
// open file for writing
136+
f, err := os.OpenFile("foo.txt", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
137+
if err != nil {
138+
// we have a call to `Sync` which precedes the call to `Close`, but there is no check
139+
// to see if `Sync` may have failed
140+
return err
141+
}
142+
defer f.Close()
143+
return f.Sync()
144+
}
145+
146+
func copyFile(destFile string, mode os.FileMode, src io.Reader) error {
147+
f, err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) // $ Source
148+
if err != nil {
149+
return err
150+
}
151+
defer f.Close() // $ SPURIOUS: Alert
152+
153+
_, err = io.Copy(f, src)
154+
if err != nil {
155+
return err
156+
}
157+
return f.Sync()
158+
}

0 commit comments

Comments
 (0)