From 97a0b9f0ca4df6bf3253ba6deb1ab95fb73e1e68 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 27 Jan 2026 14:40:23 +0000 Subject: [PATCH 1/5] (trivial) Remove double spaces in qhelp --- java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp index eb8dd211083c..e3029c6249d2 100644 --- a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp +++ b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp @@ -6,7 +6,7 @@

When a thread acquires a lock it must make sure to unlock it again; -failing to do so can lead to deadlocks. If a lock allows a thread to acquire +failing to do so can lead to deadlocks. If a lock allows a thread to acquire it multiple times, for example java.util.concurrent.locks.ReentrantLock, then the number of locks must match the number of unlocks in order to fully release the lock. @@ -17,7 +17,7 @@ release the lock.

It is recommended practice always to immediately follow a call to lock with a try block and place the call to unlock inside the -finally block. Beware of calls inside the finally block +finally block. Beware of calls inside the finally block that could cause exceptions, as this may result in skipping the call to unlock.

From 4f1ad0ff5dba26241e2860dbe0fe59a2e818fcc1 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 27 Jan 2026 15:38:06 +0000 Subject: [PATCH 2/5] Exclude *Pool classes from LockType --- java/ql/lib/semmle/code/java/Concurrency.qll | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/Concurrency.qll b/java/ql/lib/semmle/code/java/Concurrency.qll index da2783bc3080..b9dd7bfd99b0 100644 --- a/java/ql/lib/semmle/code/java/Concurrency.qll +++ b/java/ql/lib/semmle/code/java/Concurrency.qll @@ -6,12 +6,16 @@ import semmle.code.java.frameworks.Mockito /** * A Java type representing a lock. - * We identify a lock type as one that has both `lock` and `unlock` methods. + * + * We exclude types with a name ending in "Pool" as they typically manage a + * collection of resources and the `lock` and `unlock` methods typically only + * lock one resource at a time. */ class LockType extends RefType { LockType() { this.getAMethod().hasName("lock") and - this.getAMethod().hasName("unlock") + this.getAMethod().hasName("unlock") and + not this.getName().matches("%Pool") } /** Gets a method that is locking this lock type. */ From 516b84b59a194b6c798c6708868f2c16ff9ed386 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 27 Jan 2026 15:38:29 +0000 Subject: [PATCH 3/5] Add test for *Pool exclusion --- .../query-tests/UnreleasedLock/UnreleasedLock.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/java/ql/test/query-tests/UnreleasedLock/UnreleasedLock.java b/java/ql/test/query-tests/UnreleasedLock/UnreleasedLock.java index eb8de3c496d6..2aadb5044be6 100644 --- a/java/ql/test/query-tests/UnreleasedLock/UnreleasedLock.java +++ b/java/ql/test/query-tests/UnreleasedLock/UnreleasedLock.java @@ -120,4 +120,16 @@ void bad10() { } } } + + static class TestPool { + void lock() {} + void unlock() {} + } + + void good11() { + TestPool pool = new TestPool(); + pool.lock(); // Should be excluded because of "Pool" suffix + f(); + pool.unlock(); + } } From 42cbe0734ecb522d61a2fe2ac2f63ef76064a8ea Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 27 Jan 2026 15:39:03 +0000 Subject: [PATCH 4/5] Specify what lock types are considered in qhelp --- java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp index e3029c6249d2..faf03c338bdb 100644 --- a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp +++ b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.qhelp @@ -11,6 +11,11 @@ it multiple times, for example java.util.concurrent.locks.ReentrantLock +

+Any class that has both lock and unlock methods is +considered a lock type. However, classes with names ending in "Pool" are excluded, +as they typically manage a collection of resources. +

From a0c35516bd91771059c35eacb7d95fe02708ab55 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 27 Jan 2026 15:48:04 +0000 Subject: [PATCH 5/5] Add change note --- java/ql/lib/change-notes/2026-01-27-unreleased-lock-pools.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2026-01-27-unreleased-lock-pools.md diff --git a/java/ql/lib/change-notes/2026-01-27-unreleased-lock-pools.md b/java/ql/lib/change-notes/2026-01-27-unreleased-lock-pools.md new file mode 100644 index 000000000000..6ac8a19a7622 --- /dev/null +++ b/java/ql/lib/change-notes/2026-01-27-unreleased-lock-pools.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `java/unreleased-lock` no longer applies to lock types with names ending in "Pool", as these typically manage a collection of resources and the `lock` and `unlock` methods typically only lock one resource at a time. This may lead to a reduction in false positives.