Skip to content

Commit 8e6dd51

Browse files
authored
Merge pull request github#5868 from Marcono1234/marcono1234/ignore-not-closing-char-array-closeable
Java: Ignore char array based closeables for CloseReader.ql and CloseWriter.ql
2 parents 7382b34 + ef410b9 commit 8e6dd51

File tree

11 files changed

+204
-38
lines changed

11 files changed

+204
-38
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries no longer confuse `java.io` classes such as `Reader` with others that happen to share the same base name. Additionally the number of false positives has been reduced by recognizing `CharArrayReader` and `CharArrayWriter` as types that don't need to be closed.

java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ but not closed may cause a resource leak.
1414

1515
<p>Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
1616
it is safest to close a resource in a <code>finally</code> block. (However, this is unnecessary for
17-
subclasses of <code>StringReader</code> and <code>ByteArrayInputStream</code>.)
17+
subclasses of <code>CharArrayReader</code>, <code>StringReader</code> and <code>ByteArrayInputStream</code>.)
1818
</p>
1919

2020
<p>For Java 7 or later, the recommended way to close resources that implement <code>java.lang.AutoCloseable</code>

java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ import CloseType
1717

1818
predicate readerType(RefType t) {
1919
exists(RefType sup | sup = t.getASupertype*() |
20-
sup.hasName("Reader") or
21-
sup.hasName("InputStream") or
20+
sup.hasQualifiedName("java.io", ["Reader", "InputStream"]) or
2221
sup.hasQualifiedName("java.util.zip", "ZipFile")
2322
)
2423
}
2524

2625
predicate safeReaderType(RefType t) {
2726
exists(RefType sup | sup = t.getASupertype*() |
28-
sup.hasName("StringReader") or
29-
sup.hasName("ByteArrayInputStream") or
27+
sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"])
28+
or
29+
// Note: It is unclear which specific class this is supposed to match
3030
sup.hasName("StringInputStream")
3131
)
3232
}

java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ but not properly closed later may cause a resource leak.
1414

1515
<p>Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
1616
it is safest to close a resource properly in a <code>finally</code> block. (However, this is unnecessary for
17-
subclasses of <code>StringWriter</code> and <code>ByteArrayOutputStream</code>.)</p>
17+
subclasses of <code>CharArrayWriter</code>, <code>StringWriter</code> and <code>ByteArrayOutputStream</code>.)</p>
1818

1919
<p>For Java 7 or later, the recommended way to close resources that implement <code>java.lang.AutoCloseable</code>
2020
is to declare them within a <code>try-with-resources</code> statement, so that they are closed implicitly.</p>

java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@ import CloseType
1717

1818
predicate writerType(RefType t) {
1919
exists(RefType sup | sup = t.getASupertype*() |
20-
sup.hasName("Writer") or
21-
sup.hasName("OutputStream")
20+
sup.hasQualifiedName("java.io", ["Writer", "OutputStream"])
2221
)
2322
}
2423

2524
predicate safeWriterType(RefType t) {
2625
exists(RefType sup | sup = t.getASupertype*() |
27-
sup.hasName("StringWriter") or
28-
sup.hasName("ByteArrayOutputStream")
26+
sup.hasQualifiedName("java.io", ["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"])
2927
)
3028
}
3129

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
| CloseReader.java:11:42:11:71 | new FileReader(...) | This FileReader is not always closed on method exit. |
2-
| CloseReader.java:44:6:44:40 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. |
1+
| CloseReader.java:18:42:18:71 | new FileReader(...) | This FileReader is not always closed on method exit. |
2+
| CloseReader.java:23:20:23:50 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. |
3+
| CloseReader.java:33:6:33:40 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. |
4+
| CloseReader.java:43:21:43:43 | new ZipFile(...) | This ZipFile is not always closed on method exit. |
Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,73 @@
11
import java.io.BufferedReader;
2+
import java.io.ByteArrayInputStream;
3+
import java.io.CharArrayReader;
4+
import java.io.Closeable;
25
import java.io.FileInputStream;
36
import java.io.FileNotFoundException;
47
import java.io.FileReader;
5-
import java.io.IOException;
8+
import java.io.InputStream;
69
import java.io.InputStreamReader;
10+
import java.io.IOException;
11+
import java.io.Reader;
12+
import java.io.StringReader;
13+
import java.util.zip.ZipFile;
714

815
class CloseReader {
916

10-
public static void test1() throws IOException {
17+
void test1() throws IOException {
1118
BufferedReader br = new BufferedReader(new FileReader("C:\\test.txt"));
1219
System.out.println(br.readLine());
1320
}
1421

15-
public static void test2() throws FileNotFoundException, IOException {
16-
BufferedReader br = null;
22+
void test2() throws IOException {
23+
InputStream in = new FileInputStream("file.bin");
24+
in.read();
25+
}
26+
27+
void test3() throws IOException {
28+
InputStreamReader reader = null;
1729
try {
18-
br = new BufferedReader(new FileReader("C:\\test.txt"));
19-
System.out.println(br.readLine());
30+
// InputStreamReader may throw an exception, in which case the ...
31+
reader = new InputStreamReader(
32+
// ... FileInputStream is not closed by the finally block
33+
new FileInputStream("C:\\test.txt"), "UTF-8");
34+
System.out.println(reader.read());
2035
}
2136
finally {
22-
if(br != null)
23-
br.close(); // 'br' is closed
37+
if (reader != null)
38+
reader.close();
2439
}
2540
}
2641

27-
public static void test3() throws IOException {
42+
void test4() throws IOException {
43+
ZipFile zipFile = new ZipFile("file.zip");
44+
System.out.println(zipFile.getComment());
45+
}
46+
47+
void testCorrect1() throws IOException {
2848
BufferedReader br = null;
2949
try {
3050
br = new BufferedReader(new FileReader("C:\\test.txt"));
3151
System.out.println(br.readLine());
3252
}
3353
finally {
34-
cleanup(br); // 'br' is closed within a helper method
54+
if(br != null)
55+
br.close(); // 'br' is closed
3556
}
3657
}
3758

38-
public static void test4() throws IOException {
39-
InputStreamReader reader = null;
59+
void testCorrect2() throws IOException {
60+
BufferedReader br = null;
4061
try {
41-
// InputStreamReader may throw an exception, in which case the ...
42-
reader = new InputStreamReader(
43-
// ... FileInputStream is not closed by the finally block
44-
new FileInputStream("C:\\test.txt"), "UTF-8");
45-
System.out.println(reader.read());
62+
br = new BufferedReader(new FileReader("C:\\test.txt"));
63+
System.out.println(br.readLine());
4664
}
4765
finally {
48-
if (reader != null)
49-
reader.close();
66+
cleanup(br); // 'br' is closed within a helper method
5067
}
5168
}
5269

53-
public static void test5() throws IOException {
70+
void testCorrect3() throws IOException {
5471
FileInputStream fis = null;
5572
InputStreamReader reader = null;
5673
try {
@@ -66,7 +83,7 @@ public static void test5() throws IOException {
6683
}
6784
}
6885

69-
public static void test6() throws IOException {
86+
void testCorrect4() throws IOException {
7087
BufferedReader br = null;
7188
try {
7289
br = new BufferedReader(new FileReader("C:\\test.txt"));
@@ -77,15 +94,15 @@ public static void test6() throws IOException {
7794
}
7895
}
7996

80-
public static void cleanup(java.io.Closeable... closeables) throws IOException {
81-
for (java.io.Closeable c : closeables) {
97+
void cleanup(Closeable... closeables) throws IOException {
98+
for (Closeable c : closeables) {
8299
if (c != null) {
83100
c.close();
84101
}
85102
}
86103
}
87104

88-
public static class LogFile {
105+
static class LogFile {
89106
private BufferedReader fileRd;
90107
LogFile(String path) {
91108
FileReader fr = null;
@@ -100,9 +117,21 @@ public static class LogFile {
100117
private void init(InputStreamReader reader) {
101118
fileRd = new BufferedReader(reader);
102119
}
103-
public void readStuff() throws java.io.IOException {
120+
public void readStuff() throws IOException {
104121
System.out.println(fileRd.readLine());
105122
fileRd.close();
106123
}
107124
}
125+
126+
// Classes which should be ignored
127+
void testIgnore() throws IOException {
128+
Reader r1 = new CharArrayReader(new char[] {'a'});
129+
r1.read();
130+
131+
Reader r2 = new StringReader("a");
132+
r2.read();
133+
134+
InputStream i1 = new ByteArrayInputStream(new byte[] {1});
135+
i1.read();
136+
}
108137
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Likely Bugs/Resource Leaks/CloseReader.ql
1+
Likely Bugs/Resource Leaks/CloseReader.ql
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| CloseWriter.java:17:42:17:71 | new FileWriter(...) | This FileWriter is not always closed on method exit. |
2+
| CloseWriter.java:22:22:22:53 | new FileOutputStream(...) | This FileOutputStream is not always closed on method exit. |
3+
| CloseWriter.java:32:6:32:41 | new FileOutputStream(...) | This FileOutputStream is not always closed on method exit. |
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import java.io.BufferedWriter;
2+
import java.io.ByteArrayOutputStream;
3+
import java.io.CharArrayWriter;
4+
import java.io.Closeable;
5+
import java.io.FileOutputStream;
6+
import java.io.FileWriter;
7+
import java.io.IOException;
8+
import java.io.OutputStream;
9+
import java.io.OutputStreamWriter;
10+
import java.io.StringWriter;
11+
import java.io.Writer;
12+
import java.util.zip.ZipFile;
13+
14+
class CloseWriter {
15+
16+
void test1() throws IOException {
17+
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
18+
bw.write("test");
19+
}
20+
21+
void test2() throws IOException {
22+
OutputStream out = new FileOutputStream("test.bin");
23+
out.write(1);
24+
}
25+
26+
void test3() throws IOException {
27+
OutputStreamWriter writer = null;
28+
try {
29+
// OutputStreamWriter may throw an exception, in which case the ...
30+
writer = new OutputStreamWriter(
31+
// ... FileOutputStream is not closed by the finally block
32+
new FileOutputStream("C:\\test.txt"), "UTF-8");
33+
writer.write("test");
34+
}
35+
finally {
36+
if (writer != null)
37+
writer.close();
38+
}
39+
}
40+
41+
void testCorrect1() throws IOException {
42+
BufferedWriter bw = null;
43+
try {
44+
bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
45+
bw.write("test");
46+
}
47+
finally {
48+
if(bw != null)
49+
bw.close(); // 'bw' is closed
50+
}
51+
}
52+
53+
void testCorrect2() throws IOException {
54+
BufferedWriter bw = null;
55+
try {
56+
bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
57+
bw.write("test");
58+
}
59+
finally {
60+
cleanup(bw); // 'bw' is closed within a helper method
61+
}
62+
}
63+
64+
void testCorrect3() throws IOException {
65+
FileOutputStream fos = null;
66+
OutputStreamWriter writer = null;
67+
try {
68+
fos = new FileOutputStream("C:\\test.txt");
69+
writer = new OutputStreamWriter(fos);
70+
writer.write("test");
71+
}
72+
finally {
73+
if (fos != null)
74+
fos.close(); // 'fos' is closed
75+
if (writer != null)
76+
writer.close(); // 'writer' is closed
77+
}
78+
}
79+
80+
void testCorrect4() throws IOException {
81+
BufferedWriter bw = null;
82+
try {
83+
bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
84+
bw.write("test");
85+
}
86+
finally {
87+
cleanup(null, bw); // 'bw' is closed within a varargs helper method, invoked with multiple args
88+
}
89+
}
90+
91+
void cleanup(Closeable... closeables) throws IOException {
92+
for (Closeable c : closeables) {
93+
if (c != null) {
94+
c.close();
95+
}
96+
}
97+
}
98+
99+
static class LogFile {
100+
private BufferedWriter fileWr;
101+
LogFile(String path) {
102+
FileWriter fw = null;
103+
try {
104+
fw = new FileWriter(path);
105+
} catch (IOException e) {
106+
System.out.println("Error: File not readable: " + path);
107+
System.exit(1);
108+
}
109+
init(fw);
110+
}
111+
private void init(OutputStreamWriter writer) {
112+
fileWr = new BufferedWriter(writer);
113+
}
114+
public void writeStuff() throws IOException {
115+
fileWr.write("test");
116+
fileWr.close();
117+
}
118+
}
119+
120+
// Classes which should be ignored
121+
void testIgnore() throws IOException {
122+
Writer w1 = new CharArrayWriter();
123+
w1.write("test");
124+
125+
Writer w2 = new StringWriter();
126+
w2.write("test");
127+
128+
OutputStream o1 = new ByteArrayOutputStream();
129+
o1.write(1);
130+
}
131+
}

0 commit comments

Comments
 (0)