opened, never closed
Resource Leaks & Improper Shutdown
Every acquire needs a matching release. Forget it — or skip it on the error path — and file descriptors, connections, and memory pile up until the pool, the heap, or the OS limit runs out. The crash lands far from the leak, long after the request that caused it.
A handle held past its lifetime — file, socket, connection, lock — is shared state nobody released; leaks accumulate until the system starves.
In the wild
Opened, Never Closed
A resource acquired in a loop and never released exhausts a finite pool.
# SMELL: a connection per iteration, none ever returned
for row in rows:
conn = pool.acquire() # leaks: never released
conn.execute(row)
# ...pool exhausted; the next acquire() blocks forever
# RIGHT: a context manager releases on every path
for row in rows:
with pool.acquire() as conn: # returned even on exception
conn.execute(row)leak: pool drains -> acquire() hangs with: connection returned every iteration
// SMELL: response body is a file descriptor -- never closed
resp, _ := http.Get(url)
body, _ := io.ReadAll(resp.Body) // resp.Body leaks every call
// thousands of requests -> "too many open files"
// RIGHT: defer Close right after the error check
resp, err := http.Get(url)
if err != nil { return err }
defer resp.Body.Close() // released when the function returns
body, _ := io.ReadAll(resp.Body)no close: fd leak -> too many open files defer: each body closed on return
Cleanup Skipped on the Error Path
An early return or exception jumps over the release, so the happy path closes but the failure path leaks.
// SMELL: an exception before close() leaks the stream
InputStream in = new FileInputStream(path);
parse(in); // throws -> close() never runs
in.close();
// RIGHT: try-with-resources closes on every exit path
try (InputStream in = new FileInputStream(path)) {
parse(in); // close() runs even if parse throws
}manual: parse() throws -> stream leaked try-with: closed on success and on throw
/* SMELL: early return leaks the file and the buffer */
int load(const char *path) {
FILE *f = fopen(path, "r");
char *buf = malloc(N);
if (read_header(f) < 0) return -1; /* leaks f AND buf */
/* ...use buf... */
free(buf); fclose(f);
return 0;
}
/* RIGHT: single cleanup path via goto */
int load(const char *path) {
int rc = -1;
FILE *f = fopen(path, "r");
char *buf = malloc(N);
if (read_header(f) < 0) goto out;
rc = 0;
out:
free(buf); if (f) fclose(f); /* always runs */
return rc;
}early return: f and buf leaked on error goto out: every exit frees both
Where this compounds
- Pooled-Resource Heisenbug × Impure Functions
Mapped weaknesses (CWE)
On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.