Skip to content

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.

Updated 02 Nov 2026Checked against cli@de9b52a
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

  1. Release in exactly one place: the finally block in Java, a single defer in Go.
  2. 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.

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