VNX-765: Lock released twice for one acquisition
Reports a Go mutex or Java lock released a second time without being re-acquired: two unlocks in one block, an unlock in a try block repeated in its finally, and a Go explicit Unlock in a function that already defers it.
| Field | Value |
|---|---|
| Rule ID | VNX-765 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Java, Go |
| CWE | CWE-765 |
| 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 | concurrency lock multiple-unlock cwe-765 |
| Source | internal/sast/rules/vnx-765.rego |
Overview
VNX-765 maps to CWE-765: Multiple Unlocks of a Critical Resource. It reports two unlock()/Unlock()/RUnlock() statements on the same lock in one block with no re-acquisition in between, a Java unlock() inside a try block (including on an early-return branch) whose finally unlocks again, and a Go explicit Unlock() in a function that already has defer mu.Unlock() and does not re-lock afterwards.
It does not report an unlock on an early-exit branch followed by the main unlock outside it, unlock/wait/lock sequences, switch cases or if/else branches that each release once, different locks, releases inside a goroutine or lambda, or test files.
Why This Matters
Java’s ReentrantLock.unlock() throws IllegalMonitorStateException when the lock is not held, so the second release replaces the method’s result with an exception. Go’s sync.Mutex aborts the whole program with fatal error: sync: unlock of unlocked mutex, which cannot be recovered.
What Gets Flagged
lock.lock();
try {
if (items.isEmpty()) {
lock.unlock();
return null;
}
return items.remove(0);
} finally {
lock.unlock(); // flagged
}
q.mu.Lock()
defer q.mu.Unlock()
if len(q.items) == 0 {
q.mu.Unlock() // flagged
return 0, false
}
Remediation
- Release in exactly one place: the
finallyblock in Java, a singledeferin Go. - If a function must release early, do not also defer the release.
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-765
// nosec vnx-765
Or record an ignore rule: vulnetix ignore add --rule vnx-765 --reason "...". See Suppressing findings.