VNX-414: Missing Lock Check
Reports a non-blocking lock attempt whose result is discarded, so the shared resource is touched whether or not the lock is held: pthread_mutex_trylock, sem_trywait, flock with LOCK_NB, lockf F_TLOCK, fcntl F_SETLK, C++ try_lock and unchecked std::try_to_lock, Go TryLock, Java tryLock, Python acquire(False). Checked results, explicit discards, blocking locks and test code are not reported.
| Field | Value |
|---|---|
| Rule ID | VNX-414 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C, C++, Go, Java, Python |
| CWE | CWE-414 |
| 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 | missing-lock-check concurrency race-condition cwe-414 |
| Source | internal/sast/rules/vnx-414.rego |
Overview
VNX-414 maps to CWE-414: Missing Lock Check. A lock call that can fail without blocking tells the caller whether it got the lock. When that answer is thrown away, the code that follows runs whether or not the lock is held, and two threads or processes update the same state at once.
The rule reports the attempt when it is a whole statement, its result not assigned, returned, tested or passed on:
| Language | Calls |
|---|---|
| C, C++ | pthread_mutex_trylock, pthread_rwlock_tryrdlock/trywrlock, pthread_spin_trylock, mtx_trylock, sem_trywait, pthread_mutex_timedlock, sem_timedwait, flock(fd, LOCK_EX/LOCK_SH | LOCK_NB), lockf(fd, F_TLOCK, ...), fcntl(fd, F_SETLK, ...) |
| C++ | m.try_lock(), try_lock_for, try_lock_until, std::try_lock(a, b); a std::unique_lock built with std::try_to_lock whose owns_lock() (or the lock itself) is not tested in the next 10 lines |
| Go | mu.TryLock(), rw.TryRLock() |
| Java | lock.tryLock(...), channel.tryLock(...) |
| Python | lock.acquire(False), acquire(blocking=False), acquire(block=False) (multiprocessing), acquire with a finite timeout (a negative or None timeout waits forever and is not reported) |
What Gets Flagged
pthread_mutex_trylock(&stats_lock); // FLAGGED: may not hold the lock
s->hits++;
c.mu.TryLock() // FLAGGED
c.data[k] = v
self.lock.acquire(blocking=False) # FLAGGED
self.active += 1
What Is Not Flagged
- Results that are tested, assigned or returned:
if (pthread_mutex_trylock(&m) != 0) return;,if !mu.TryLock() {,got = lock.acquire(False). - An explicit discard:
(void)pthread_mutex_trylock(&m)in C,_ = mu.TryLock()in Go. - Blocking locks (
pthread_mutex_lock,flockwithoutLOCK_NB,fcntl(F_SETLKW),Lock()), unlocks (LOCK_UN), test files, comments and string literals.
Remediation
Proceed only when the attempt succeeded:
if (pthread_mutex_trylock(&stats_lock) != 0)
return; /* busy: try again later */
s->hits++;
pthread_mutex_unlock(&stats_lock);
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-414
// nosec vnx-414
Or record an ignore rule: vulnetix ignore add --rule vnx-414 --reason "...". See Suppressing findings.