Skip to end of metadata
Go to start of metadata

The C++ Standard Library supplies both recursive and non-recursive mutex classes used to protect critical sections. The recursive mutex classes (std::recursive_mutex and std::recursive_timed_mutex) differ from the non-recursive mutex classes (std::mutexstd::timed_mutex, and std::shared_timed_mutex) in that a recursive mutex may be locked recursively by the thread that currently owns the mutex. All mutex classes support the ability to speculatively lock the mutex through functions such as try_lock()try_lock_for()try_lock_until()try_lock_shared_for(), and try_lock_shared_until(). These speculative locking functions attempt to obtain ownership of the mutex for the calling thread, but will not block in the event the ownership cannot be obtained. Instead, they return a Boolean value specifying whether the ownership of the mutex was obtained or not.

The C++ Standard, [thread.mutex.requirements.mutex], paragraphs 14 and 15 [ISO/IEC 14882-2014], state the following:

The expression m.try_lock() shall be well-formed and have the following semantics:
Requires: If m is of type std::mutex, std::timed_mutex, or std::shared_timed_mutex, the calling thread does not own the mutex.

Further, [thread.timedmutex.class], paragraph 3, in part, states the following:

The behavior of a program is undefined if:
— a thread that owns a timed_mutex object calls lock(), try_lock(), try_lock_for(), or try_lock_until() on that object

Finally, [thread.sharedtimedmutex.class], paragraph 3, in part, states the following:

The behavior of a program is undefined if:
— a thread attempts to recursively gain any ownership of a shared_timed_mutex.

Thus, attempting to speculatively lock a non-recursive mutex object that is already owned by the calling thread is undefined behavior. Do not call try_lock()try_lock_for()try_lock_until()try_lock_shared_for(), or try_lock_shared_until() on a non-recursive mutex object from a thread that already owns that mutex object.

Noncompliant Code Example

In this noncompliant code example, the mutex m is locked by the thread's initial entry point and is speculatively locked in the do_work() function from the same thread, resulting in undefined behavior because it is not a recursive mutex. With common implementations, this may result in deadlock.

#include <mutex>
#include <thread>

std::mutex m;

void do_thread_safe_work();

void do_work() {
  while (!m.try_lock()) {
    // The lock is not owned yet, do other work while waiting.
    do_thread_safe_work();
  }
  try {

    // The mutex is now locked; perform work on shared resources.
    // ...

  // Release the mutex.
  catch (...) {
    m.unlock();
    throw;
  }
  m.unlock();
}

void start_func() {
  std::lock_guard<std::mutex> lock(m);
  do_work();
}

int main() {
  std::thread t(start_func);

  do_work();

  t.join();
}

Compliant Solution

This compliant solution removes the lock from the thread's initial entry point, allowing the mutex to be speculatively locked, but not recursively.

#include <mutex>
#include <thread>

std::mutex m;

void do_thread_safe_work();

void do_work() {
  while (!m.try_lock()) {
    // The lock is not owned yet, do other work while waiting.
    do_thread_safe_work();
  }
  try {
    // The mutex is now locked; perform work on shared resources.
    // ...

  // Release the mutex.
  catch (...) {
    m.unlock();
    throw;
  }
  m.unlock();
}

void start_func() {
  do_work();
}

int main() {
  std::thread t(start_func);

  do_work();

  t.join();
}

Risk Assessment

Speculatively locking a non-recursive mutex in a recursive manner is undefined behavior that can lead to deadlock.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON56-CPP

Low

Unlikely

High

P1

L3

Automated Detection

Tool

Version

Checker

Description

Parasoft C/C++test

10.4.2

CERT_CPP-CON56-a

Avoid double locking

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

MITRE CWECWE-667, Improper Locking

Bibliography

[ISO/IEC 14882-2014]Subclause 30.4.1, "Mutex Requirements"