ASVS 5.0 V15.4.3 requires locks to be used consistently so threads do not get stuck waiting on each other or retrying forever, and requires locking logic to stay inside the code that manages the resource so outside code cannot alter it. It is Level 3. Use a fixed lock order, timeouts, and private lock objects.
Why it matters
A deadlock is an availability failure an attacker can trigger by sending the right request mix. A lock that other classes can grab becomes a way to freeze your service on purpose.
How to implement it
Keep the lock private and always take locks in the same order. Use a timeout instead of waiting forever.
public class Ledger {
private final ReentrantLock lock = new ReentrantLock(); // private, never exposed
public boolean transfer(Account a, Account b, long amt) throws InterruptedException {
Account first = a.id() < b.id() ? a : b; // global ordering rule
Account second = first == a ? b : a;
if (!first.lock().tryLock(2, TimeUnit.SECONDS)) return false;
try {
if (!second.lock().tryLock(2, TimeUnit.SECONDS)) return false;
try { return apply(a, b, amt); } finally { second.lock().unlock(); }
} finally { first.lock().unlock(); }
}
}
Never synchronized on this or on an object callers also hold.
How to test it
Run opposite direction operations concurrently, such as transfer A to B alongside B to A, many times, and require that all finish within a deadline. Take thread dumps if one stalls.
Common mistakes
- Locking on public objects or interned strings.
- Different lock orders in different methods.
- Retry loops with no backoff or cap.
Quick check
Why use tryLock with a timeout?
It turns a permanent hang into a handled failure you can log and retry.