False Negative: CloseSql.ql misses leaked JDBC resources once the allocation site is split across helper calls or `getResultSet()`.
- Dominant language
- CodeQL
- Stars
- 10.1k
- Forks
- 2.1k
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 141
Description
Version
codeql 2.24.3
## Checker
- Checker id: `Likely Bugs/Resource Leaks/CloseSql.ql`
- Checker description: This checker detects SQL resource objects (Connection, Statement, ResultSet) that are initialized locally and not guaranteed to be closed on method exit.
## Description of the false negative
These cases still leak JDBC resources. One is the simplest possible leaked `Connection`. The other two leak `ResultSet` instances that come from a `Statement` parameter and are never closed before the method returns.
What changes between the samples is only how the resource is obtained: directly, through a helper method, or through `Statement.getResultSet()` after `execute(...)`.
## Affected test cases
### `PosCase1.java`
The method opens a `Connection` and exits without closing it. This should be a baseline match for the rule.
### `PosCase6_Var3.java`
The `ResultSet` comes back from a helper, but it still originates from the passed-in `Statement` and still leaks.
### `PosCase6_Var4.java`
The `ResultSet` is retrieved through `stmt.getResultSet()` after `execute(...)`. That is still a live SQL resource that needs to be closed.
## Reproduction code
### `PosCase1.java`
```java
// Scenario 1: A java.sql.Connection is locally initialized, not assigned, not passed to a local constructor, has no parent, and is not closed.
package scensct.core.pos;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class PosCase1 {
public void test() throws SQLException {
// Locally initialized Connection, assigned to a variable but not closed.
Connection conn = DriverManager.getConnection("jdbc:example:db");
// No close() call before method exit.
}
}
```
### `PosCase6_Var3.java`
```java
// Scenario 6: A java.sql.ResultSet is locally initialized as a child of a non-locally-initialized Statement parameter, used directly, and not closed.
package scensct.var.pos;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class PosCase6_Var3 {
public void test(Statement stmt) throws SQLException {
ResultSet rs = fetchResult(stmt);
// rs not closed
}
private ResultSet fetchResult(Statement s) throws SQLException {
return s.executeQuery("SELECT 1");
}
}
```
### `PosCase6_Var4.java`
```java
// Scenario 6: A java.sql.ResultSet is locally initialized as a child of a non-locally-initialized Statement parameter, used directly, and not closed.
package scensct.var.pos;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class PosCase6_Var4 {
public void test(Statement stmt) throws SQLException {
boolean hasResults = stmt.execute("SELECT 1");
if (hasResults) {
ResultSet rs = stmt.getResultSet();
// rs not closed
}
}
}
```
## Cause analysis
`Likely Bugs/Resource Leaks/CloseSql.ql` appears to be tied too closely to one acquisition shape. It handles some direct JDBC constructions, but coverage gets weaker once the returned resource is produced through a helper or through a second-stage API like `getResultSet()`.
That leaves a real blind spot. In production JDBC code, `ResultSet` objects are often obtained indirectly rather than from a single inline `executeQuery(...)` call. The leak is still the same: the method owns a SQL resource and does not close it.
Contributor guide
Research direction
Start with Likely Bugs/Resource Leaks/CloseSql.ql and compare its acquisition handling with the affected PosCase1.java, PosCase6_Var3.java, and PosCase6_Var4.java examples. Verify the checker reports the leaked Connection and both indirectly obtained ResultSet instances, while preserving existing coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- devtools, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100