Mutexes are used to protect shared data structures from being concurrently accessed. If a mutex is destroyed while a thread is blocked waiting for that mutex, critical sections and shared data are no longer protected.
The C++ Standard, [thread.mutex.class], paragraph 5 [ISO/IEC 14882-2014], states:
The behavior of a program is undefined if it destroys a
mutexobject owned by any thread or a thread terminates while owning amutexobject.
This statement implies that destroying a mutex while a thread is waiting on it is undefined behavior.
Noncompliant Code Example
This noncompliant code example creates several threads that each invoke the do_work() function, passing a unique number as an ID.
Unfortunately, this code contains a race condition, allowing the mutex to be destroyed before it is unlocked, because start_threads() may invoke the lock's destructor before all of the threads have finished using the lock. This behavior is undefined.
#include <mutex>
#include <thread>
const size_t max_threads = 10;
void do_work(size_t i, std::mutex *pm) {
std::lock_guard<std::mutex> guard(*pm);
// Access data protected by the lock.
}
void start_threads() {
std::thread threads[max_threads];
std::mutex m;
for (size_t i = 0; i < max_threads; ++i) {
threads[i] = std::thread(do_work, i, &m);
}
}
Compliant Solution
This compliant solution eliminates the race condition by extending the lifetime of the lock:
#include <mutex>
#include <thread>
const size_t max_threads = 10;
void do_work(size_t i, std::mutex *pm) {
std::lock_guard<std::mutex> guard(*pm);
// Access data protected by the lock.
}
std::mutex m;
void start_threads() {
std::thread threads[max_threads];
for (size_t i = 0; i < max_threads; ++i) {
threads[i] = std::thread(do_work, i, &m);
}
}
Compliant Solution
This compliant solution eliminates the race condition by joining the threads before the mutex's destructor is invoked:
#include <mutex>
#include <thread>
const size_t max_threads = 10;
void do_work(size_t i, std::mutex *pm) {
std::lock_guard<std::mutex> guard(*pm);
// Access data protected by the lock.
}
void run_threads() {
std::thread threads[max_threads];
std::mutex m;
for (size_t i = 0; i < max_threads; ++i) {
threads[i] = std::thread(do_work, i, &m);
}
for (size_t i = 0; i < max_threads; ++i) {
threads[i].join();
}
}
Risk Assessment
Destroying a mutex while it is locked may result in invalid control flow and data corruption.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
CON50-CPP | Medium | Probable | High | P4 | L3 |
Automated Detection
Tool | Version | Checker | Description |
|---|---|---|---|
| |||
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
| [ISO/IEC 14882-2014] | [thread.mutex] "Mutual exclusion" |