Skip to content

VNX-667: Improper locking: shared String/boxed monitor or Lock released outside finally

Reports Java synchronized blocks on interned Strings or boxed values, and Lock.lock() calls with no finally block that unlocks the same lock.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-667
Severity Low
SARIF level note
Kind sast
Languages Java
CWE CWE-667
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags improper-locking cwe-667 java concurrency
Source internal/sast/rules/vnx-667.rego

Overview

VNX-667 maps to CWE-667: Improper Locking. It reports two Java locking mistakes that compile, look correct and still fail:

  1. A shared monitor. synchronized on a String literal, an .intern()ed string, Boolean.TRUE/FALSE, a valueOf(...) box, or a field, variable or method parameter the file declares as String, Boolean, Integer, Long, Short, Byte or Character. Interned strings and cached boxes are the same object everywhere in the JVM, so unrelated code (another library, another tenant) contends on, or deadlocks with, your lock. A boxed field reassigned inside the block (generation++) changes the monitor while it is held.
  2. A lock with no finally. A statement lock.lock() or lockInterruptibly() (also rw.readLock().lock()) with no finally in the next 60 lines that calls the same receiver’s unlock(). An exception in the critical section leaves the lock held forever.

What Gets Flagged

private static final String LOCK = "LOCK";
synchronized (LOCK) { n++; }                 // FLAGGED: interned literal
synchronized (tenant.intern()) { ... }        // FLAGGED
synchronized (Boolean.TRUE) { ... }           // FLAGGED
lock.lock();                                  // FLAGGED: unlock() is skipped if this throws
if (amount > balance) throw new IllegalStateException();
balance -= amount;
lock.unlock();

What Is Not Flagged

  • synchronized (this), synchronized (Foo.class), or a private Object field.
  • lock.lock(); try { ... } finally { lock.unlock(); }, and try { lock.lock(); ... } finally { lock.unlock(); }.
  • tryLock(), FileChannel.lock() assignments, methods that are themselves named lock/acquire (wrappers).
  • Test files (for the Lock shape), comments and string literals.

Remediation

private final Object lock = new Object();
synchronized (lock) { ... }

lock.lock();
try {
    ...
} finally {
    lock.unlock();
}

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-667
// nosec vnx-667

Or record an ignore rule: vulnetix ignore add --rule vnx-667 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-667.rego, cli/internal/sast/docs/vnx-667.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.