CertC++-CON51¶
Ensure actively held locks are released on exceptional conditions
Required inputs: IR, StaticSemanticAnalysis
Mutexes that are used to protect accesses to shared data may be locked using
the
lock() member function and unlocked
using the
unlock() member function. If an
exception occurs between the call to
lock() and the call to
unlock(), and the exception changes
control flow such that
unlock() is not called, the mutex
will be left in the locked state and no
critical
sections protected by that mutex will be allowed to execute. This is likely
to lead to deadlock.
The throwing of an exception must not allow a mutex to remain locked indefinitely. If a mutex was locked and an exception occurs within the critical section protected by that mutex, the mutex must be unlocked as part of exception handling before rethrowing the exception or continuing execution unless subsequent control flow will unlock the mutex.
C++ supplies the lock classes
lock_guard,
unique_lock, and
shared_lock, which can be initialized
with a mutex. In its constructor, the lock object locks the mutex, and in
its destructor, it unlocks the mutex. The
lock_guard class provides a simple
RAII
wrapper around a mutex. The
unique_lock and
shared_lock classes also use RAII and provide additional
functionality, such as manual control over the locking strategy. The
unique_lock class prevents the lock from being copied, although it
allows the lock ownership to be moved to another lock. The
shared_lock class allows the mutex to be shared by several locks.
For all three classes, if an exception occurs and takes control flow out of the
scope of the lock, the destructor will unlock the mutex and the program can
continue working normally. These lock objects are the preferred way to ensure
that a mutex is properly released when an exception is thrown.
Noncompliant Code Example
This noncompliant code example manipulates shared data and protects the critical section by locking the mutex. When it is finished, it unlocks the mutex. However, if an exception occurs while manipulating the shared data, the mutex will remain locked.
#include <mutex>
void manipulate_shared_data(std::mutex &pm) {
pm.lock();
// Perform work on shared data.
pm.unlock();
}
Compliant Solution (Manual Unlock)
This compliant solution catches any exceptions thrown when performing work on the shared data and unlocks the mutex before rethrowing the exception.
#include <mutex>
void manipulate_shared_data(std::mutex &pm) {
pm.lock();
try {
// Perform work on shared data.
} catch (...) {
pm.unlock();
throw;
}
pm.unlock(); // in case no exceptions occur
}
Compliant Solution (Lock Object)
This compliant solution uses a
lock_guard object to ensure that the mutex will be unlocked, even
if an exception occurs, without relying on exception handling machinery and
manual resource management.
#include <mutex>
void manipulate_shared_data(std::mutex &pm) {
std::lock_guard<std::mutex> lk(pm);
// Perform work on shared data.
}
Risk Assessment
If an exception occurs while a mutex is locked, deadlock may result.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| CON51-CPP | Low | Probable | Low | P6 | L2 |
Related Guidelines
This rule is a subset of ERR56-CPP. Guarantee exception safety.
| MITRE CWE | CWE-667, Improper Locking |
| SEI CERT Oracle Coding Standard for Java | LCK08-J. Ensure actively held locks are released on exceptional conditions |
Bibliography
| [ ISO/IEC 14882-2014] | Subclause 30.4.2, "Locks" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
possible_lock_held_on_exception |
Actively held locks not released on exceptional conditions. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
mutex_lock_routines¶
mutex_lock_routines : set[bauhaus.analysis.config.QualifiedName] = {'std::_Mutex_base::lock', 'std::mutex::lock'}
mutex_unlock_routines¶
mutex_unlock_routines : set[bauhaus.analysis.config.QualifiedName] = {'std::_Mutex_base::unlock', 'std::mutex::unlock'}