原文链接:

CON50-CPP. Do not destroy a mutex while it is locked


互斥锁对象被用于防止并发访问共享数据。如果一个互斥锁对象在线程等待锁被阻塞时被销毁,临界区和共享数据将不再受到保护。 C++标准 [thread.mutex.class],第5段 [ISO/IEC 14882-2014] 规定如下:

如果程序销毁了任何线程所拥有的互斥锁对象或一个线程在拥有互斥锁对象时终止,那么程序的行为是未定义的.

类似措辞也适用于 std::recursive_mutexstd::timed_mutexstd::recursive_timed_mutexstd::shared_timed_mutex 。这些声明意味着在线程等待互斥锁的时销毁互斥锁对象是未定义的行为

不合规代码示例

这个不合规的代码示例创建了几个线程,每个线程都调用 do_work() 函数,传递一个唯一的数字作为 ID。

不幸的是,这段代码包含了一个竞态条件,允许销毁仍被占有的互斥锁还,因为 start_threads() 可能在所有线程退出之前调用互斥锁的析构函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <mutex>
#include <thread>

const size_t maxThreads = 10;

void do_work(size_t i, std::mutex *pm) {
std::lock_guard<std::mutex> lk(*pm);

// Access data protected by the lock.
}

void start_threads() {
std::thread threads[maxThreads];
std::mutex m;

for (size_t i = 0; i < maxThreads; ++i) {
threads[i] = std::thread(do_work, i, &m);
}
}

合规解决方案

这个合规的解决方案通过延长互斥对象的生命周期消除了竞态条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <mutex>
#include <thread>

const size_t maxThreads = 10;

void do_work(size_t i, std::mutex *pm) {
std::lock_guard<std::mutex> lk(*pm);

// Access data protected by the lock.
}

std::mutex m;

void start_threads() {
std::thread threads[maxThreads];

for (size_t i = 0; i < maxThreads; ++i) {
threads[i] = std::thread(do_work, i, &m);
}
}

合规解决方案

这个合规的解决方案在互斥对象的析构函数被调用前, 通过 joining 线程消除了竞态条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <mutex>
#include <thread>

const size_t maxThreads = 10;

void do_work(size_t i, std::mutex *pm) {
std::lock_guard<std::mutex> lk(*pm);

// Access data protected by the lock.
}

void run_threads() {
std::thread threads[maxThreads];
std::mutex m;

for (size_t i = 0; i < maxThreads; ++i) {
threads[i] = std::thread(do_work, i, &m);
}

for (size_t i = 0; i < maxThreads; ++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
CodeSonar 7.3p0 CONCURRENCY.LOCALARG Local Variable Passed to Thread
Helix QAC 2023.1 DF961, DF4962
Klocwork 2023.1 CERT.CONC.MUTEX.DESTROY_WHILE_LOCKED
Parasoft C/C++test 2022.2 CERT_CPP-CON50-a Do not destroy another thread’s mutex
Polyspace Bug Finder R2023a CERT C++: CON50-CPP Checks for destruction of locked mutex (rule partially covered)
PRQA QA-C++ 4.4 4961, 4962

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

MITRE CWE CWE-667, Improper Locking
SEI CERT C Coding Standard CON31-C. Do not destroy a mutex while it is locked

Bibliography

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

img img img