Skip to content

Commit a201a99

Browse files
committed
thread-safety: fix annotations with REVERSE_LOCK
Without proper annotations, clang thinks that mutexes are still held for the duration of a reverse_lock. This could lead to subtle bugs as EXCLUSIVE_LOCKS_REQUIRED(foo) passes when it shouldn't. As mentioned in the docs [0], clang's thread-safety analyzer is unable to deal with aliases of mutexes, so it is not possible to use the lock's copy of the mutex for that purpose. Instead, the original mutex needs to be passed back to the reverse_lock for the sake of thread-safety analysis, but it is not actually used otherwise. [0]: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
1 parent aeea5f0 commit a201a99

File tree

5 files changed

+21
-13
lines changed

5 files changed

+21
-13
lines changed

src/node/interfaces.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<Rec
443443
if (block.m_locator) { *block.m_locator = GetLocator(index); }
444444
if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active, blockman);
445445
if (block.m_data) {
446-
REVERSE_LOCK(lock);
446+
REVERSE_LOCK(lock, cs_main);
447447
if (!blockman.ReadBlock(*block.m_data, *index)) block.m_data->SetNull();
448448
}
449449
block.found = true;

src/scheduler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void CScheduler::serviceQueue()
5656
{
5757
// Unlock before calling f, so it can reschedule itself or another task
5858
// without deadlocking:
59-
REVERSE_LOCK(lock);
59+
REVERSE_LOCK(lock, newTaskMutex);
6060
f();
6161
}
6262
} catch (...) {

src/sync.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <threadsafety.h> // IWYU pragma: export
1515
#include <util/macros.h>
1616

17+
#include <cassert>
1718
#include <condition_variable>
1819
#include <mutex>
1920
#include <string>
@@ -212,16 +213,19 @@ class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
212213
/**
213214
* An RAII-style reverse lock. Unlocks on construction and locks on destruction.
214215
*/
215-
class reverse_lock {
216+
class SCOPED_LOCKABLE reverse_lock {
216217
public:
217-
explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
218+
explicit reverse_lock(UniqueLock& _lock, const MutexType& mutex, const char* _guardname, const char* _file, int _line) UNLOCK_FUNCTION(mutex) : lock(_lock), file(_file), line(_line) {
219+
// Ensure that mutex passed back for thread-safety analysis is indeed the original
220+
assert(std::addressof(mutex) == lock.mutex());
221+
218222
CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
219223
lock.unlock();
220224
LeaveCritical();
221225
lock.swap(templock);
222226
}
223227

224-
~reverse_lock() {
228+
~reverse_lock() UNLOCK_FUNCTION() {
225229
templock.swap(lock);
226230
EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
227231
lock.lock();
@@ -240,7 +244,11 @@ class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
240244
friend class reverse_lock;
241245
};
242246

243-
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
247+
// clang's thread-safety analyzer is unable to deal with aliases of mutexes, so
248+
// it is not possible to use the lock's copy of the mutex for that purpose.
249+
// Instead, the original mutex needs to be passed back to the reverse_lock for
250+
// the sake of thread-safety analysis, but it is not actually used otherwise.
251+
#define REVERSE_LOCK(g, cs) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, cs, #g, __FILE__, __LINE__)
244252

245253
// When locking a Mutex, require negative capability to ensure the lock
246254
// is not already held

src/test/reverselock_tests.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(reverselock_basics)
1818

1919
BOOST_CHECK(lock.owns_lock());
2020
{
21-
REVERSE_LOCK(lock);
21+
REVERSE_LOCK(lock, mutex);
2222
BOOST_CHECK(!lock.owns_lock());
2323
}
2424
BOOST_CHECK(lock.owns_lock());
@@ -33,9 +33,9 @@ BOOST_AUTO_TEST_CASE(reverselock_multiple)
3333

3434
// Make sure undoing two locks succeeds
3535
{
36-
REVERSE_LOCK(lock);
36+
REVERSE_LOCK(lock, mutex);
3737
BOOST_CHECK(!lock.owns_lock());
38-
REVERSE_LOCK(lock2);
38+
REVERSE_LOCK(lock2, mutex2);
3939
BOOST_CHECK(!lock2.owns_lock());
4040
}
4141
BOOST_CHECK(lock.owns_lock());
@@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
5454
g_debug_lockorder_abort = false;
5555

5656
// Make sure trying to reverse lock a previous lock fails
57-
BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock2), std::logic_error, HasReason("lock2 was not most recent critical section locked"));
57+
BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock2, mutex2), std::logic_error, HasReason("lock2 was not most recent critical section locked"));
5858
BOOST_CHECK(lock2.owns_lock());
5959

6060
g_debug_lockorder_abort = prev;
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
6767

6868
bool failed = false;
6969
try {
70-
REVERSE_LOCK(lock);
70+
REVERSE_LOCK(lock, mutex);
7171
} catch(...) {
7272
failed = true;
7373
}
@@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
8282
lock.lock();
8383
BOOST_CHECK(lock.owns_lock());
8484
{
85-
REVERSE_LOCK(lock);
85+
REVERSE_LOCK(lock, mutex);
8686
BOOST_CHECK(!lock.owns_lock());
8787
}
8888

src/validationinterface.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class ValidationSignalsImpl
8383
for (auto it = m_list.begin(); it != m_list.end();) {
8484
++it->count;
8585
{
86-
REVERSE_LOCK(lock);
86+
REVERSE_LOCK(lock, m_mutex);
8787
f(*it->callbacks);
8888
}
8989
it = --it->count ? std::next(it) : m_list.erase(it);

0 commit comments

Comments
 (0)