Lock not locked
| Vulnerability potential | Low |
| DDoS potential | Medium |
The mutex is not locked when unlocking
Impact
A thread calls unlock on a mutex that is not currently locked, or that is locked by a different thread. POSIX makes both cases undefined for a default mutex. The visible effect is a violation of the ownership discipline mutexes rely on: unlocking a mutex held by another thread hands that thread’s critical section to everyone, so two or more threads execute protected code at once and race on the data it guards. Unlocking a never-locked mutex tends to drive its internal counter or futex word out of its valid range, leaving the mutex in a state where later locks deadlock, return errors, or are granted incorrectly. The defect usually originates in error-handling paths that unlock without a matching lock, or in code that unlocks based on a flag that was set wrong.
Vulnerability potential
Like the other mutex misuse defects, this is chiefly an availability and correctness issue with a secondary memory-safety tail.
- Releasing a lock the caller does not own destroys mutual exclusion, allowing concurrent access to the protected structure and the races (use-after-free, double-free, torn state) that can follow.
- Corrupting the mutex’s bookkeeping can wedge future lock attempts, hanging threads and denying service.
- The operation is undefined, so on stricter implementations it aborts the process outright.
Technical details
Mutual exclusion depends on the rule that a mutex is unlocked only by the thread that locked it, and only while it is held. Whether a violation is caught depends on the mutex type:
PTHREAD_MUTEX_NORMAL / default
There is no ownership record, so the runtime cannot tell that the caller never locked the mutex; it performs the release anyway, with undefined results on the mutex state and on any other thread that holds it.
PTHREAD_MUTEX_ERRORCHECK
The owner is tracked, so unlocking an unlocked mutex or one owned by another
thread returns EPERM rather than corrupting state — ideal for catching the
bug in testing.
The same ownership requirement holds for C++ std::mutex (unlocking a
non-owned mutex is UB), C11 mtx_unlock, and Windows ReleaseMutex (which
fails with ERROR_NOT_OWNER for a non-owner). RAII guards exist specifically
so that an unlock cannot happen without a preceding lock on the same path.
Catching the issue
Runtime
ThreadSanitizer reports unlock of an unlocked mutex and unlocks by a
non-owning thread. Helgrind and DRD flag the same. Switching test builds to
PTHREAD_MUTEX_ERRORCHECK converts the undefined behavior into an observable
EPERM you can assert on.
Static analysis and design
Clang Thread Safety Analysis (-Wthread-safety), Coverity and PVS-Studio
track lock/unlock balance per control-flow path and flag an unlock with no
dominating lock. Structurally pair locks and unlocks with scope guards
(std::lock_guard) or pthread_cleanup_push to make the defect impossible.
How to reproduce
Build with -fsanitize=thread or use an error-checking mutex; the unlock of a
freshly initialized, never-locked mutex is reported (or returns EPERM).
#include <pthread.h>
#include <stdio.h>
int main(void)
{
pthread_mutexattr_t a;
pthread_mutex_t m;
pthread_mutexattr_init(&a);
pthread_mutexattr_settype(&a, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&m, &a);
/* Unlock without ever locking. */
printf("rc: %d (EPERM expected)\n", pthread_mutex_unlock(&m));
return 0;
}