Skip to content

Commit 4483a24

Browse files
authored
Merge pull request #19075 from jcogs33/jcogs33/java/do-not-use-finalizers
Java: Add new quality query to detect `finalize` calls
2 parents ed99088 + 3aa6b49 commit 4483a24

File tree

7 files changed

+122
-0
lines changed

7 files changed

+122
-0
lines changed

java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ ql/java/ql/src/Likely Bugs/Likely Typos/SuspiciousDateFormat.ql
1111
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
1212
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
1313
ql/java/ql/src/Performance/StringReplaceAllWithNonRegex.ql
14+
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
## Overview
2+
3+
Triggering garbage collection by directly calling `finalize()` may either have no effect or trigger unnecessary garbage collection, leading to erratic behavior, performance issues, or deadlock.
4+
5+
## Recommendation
6+
7+
Avoid calling `finalize()` in application code. Allow the JVM to determine a garbage collection schedule instead. If you need to explicitly release resources, provide a specific method to do so, such as by implementing the `AutoCloseable` interface and overriding its `close` method. You can then use a `try-with-resources` block to ensure that the resource is closed.
8+
9+
## Example
10+
11+
```java
12+
class LocalCache {
13+
private Collection<File> cacheFiles = ...;
14+
// ...
15+
}
16+
17+
void main() {
18+
LocalCache cache = new LocalCache();
19+
// ...
20+
cache.finalize(); // NON_COMPLIANT
21+
}
22+
23+
```
24+
25+
```java
26+
import java.lang.AutoCloseable;
27+
import java.lang.Override;
28+
29+
class LocalCache implements AutoCloseable {
30+
private Collection<File> cacheFiles = ...;
31+
// ...
32+
33+
@Override
34+
public void close() throws Exception {
35+
// release resources here if required
36+
}
37+
}
38+
39+
void main() {
40+
// COMPLIANT: uses try-with-resources to ensure that
41+
// a resource implementing AutoCloseable is closed.
42+
try (LocalCache cache = new LocalCache()) {
43+
// ...
44+
}
45+
}
46+
47+
```
48+
49+
# Implementation Notes
50+
51+
This rule ignores `super.finalize()` calls that occur within `finalize()` overrides since calling the superclass finalizer is required when overriding `finalize()`. Also, although overriding `finalize()` is not recommended, this rule only alerts on direct calls to `finalize()` and does not alert on method declarations overriding `finalize()`.
52+
53+
## References
54+
55+
- SEI CERT Oracle Coding Standard for Java: [MET12-J. Do not use finalizers](https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers).
56+
- Java API Specification: [Object.finalize()](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#finalize()).
57+
- Java API Specification: [Interface AutoCloseable](https://docs.oracle.com/javase/10/docs/api/java/lang/AutoCloseable.html).
58+
- Java SE Documentation: [The try-with-resources Statement](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html).
59+
- Common Weakness Enumeration: [CWE-586](https://cwe.mitre.org/data/definitions/586).
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* @id java/do-not-call-finalize
3+
* @previous-id java/do-not-use-finalizers
4+
* @name Do not call `finalize()`
5+
* @description Calling `finalize()` in application code may cause
6+
* inconsistent program state or unpredictable behavior.
7+
* @kind problem
8+
* @precision high
9+
* @problem.severity error
10+
* @tags quality
11+
* reliability
12+
* correctness
13+
* performance
14+
* external/cwe/cwe-586
15+
*/
16+
17+
import java
18+
19+
from MethodCall mc
20+
where
21+
mc.getMethod() instanceof FinalizeMethod and
22+
// The Java documentation for `finalize()` states: "If a subclass overrides
23+
// `finalize` it must invoke the superclass finalizer explicitly". Therefore,
24+
// we do not alert on `super.finalize()` calls that occur within a callable
25+
// that overrides `finalize`.
26+
not exists(Callable caller, FinalizeMethod fm | caller = mc.getCaller() |
27+
caller.(Method).overrides(fm) and
28+
mc.getQualifier() instanceof SuperAccess
29+
)
30+
select mc, "Call to 'finalize()'."

java/ql/src/codeql-suites/java-code-quality.qls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- include:
33
id:
44
- java/contradictory-type-checks
5+
- java/do-not-call-finalize
56
- java/equals-on-unrelated-types
67
- java/inconsistent-equals-and-hashcode
78
- java/input-resource-leak
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| Test.java:4:9:4:23 | finalize(...) | Call to 'finalize()'. |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
public class Test {
2+
void f() throws Throwable {
3+
// NON_COMPLIANT
4+
this.finalize(); // $ Alert
5+
}
6+
7+
void f1() throws Throwable {
8+
f(); // COMPLIANT
9+
}
10+
11+
@Override
12+
protected void finalize() throws Throwable {
13+
// COMPLIANT: If a subclass overrides `finalize()`
14+
// it must invoke the superclass finalizer explicitly.
15+
super.finalize();
16+
}
17+
18+
// Overload of `finalize`
19+
protected void finalize(String s) throws Throwable {
20+
// ...
21+
}
22+
23+
void f2() throws Throwable {
24+
// COMPLIANT: call to overload of `finalize`
25+
this.finalize("overload");
26+
}
27+
28+
}

0 commit comments

Comments
 (0)