Skip to content

Commit 8df816f

Browse files
authored
Fix ABA and UAF problem in ConditionVariable notify operation using internal spinlock (#458) (#459)
* Fix ABA problem in ConditionVariable::notifyOne using internal spinlock (Fixes #458) * Fix UAF and ABA in ConditionVariable notify using SpinLock (Fixes #458) Serialize notifyOne and notifyAll with an internal SpinLock (MPSC model) to prevent Use-After-Free and ABA memory corruption during concurrent pops, while preserving the lock-free performance of wait(). * fix(ci): resolve clang-format warnings and C++20 module dependency * style: bypass clang-format for module dependency order
1 parent e50c0a8 commit 8df816f

2 files changed

Lines changed: 25 additions & 11 deletions

File tree

async_simple/async_simple.cppm

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ export extern "C++" {
5858
#include "uthread/internal/thread.h"
5959
#include "uthread/Uthread.h"
6060
#include "uthread/Async.h"
61-
#include "coro/ConditionVariable.h"
61+
// clang-format off
6262
#include "coro/SpinLock.h"
63+
#include "coro/ConditionVariable.h"
64+
// clang-format on
6365
#include "coro/Latch.h"
6466
#include "coro/CountEvent.h"
6567
#include "coro/Collect.h"

async_simple/coro/ConditionVariable.h

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef ASYNC_SIMPLE_USE_MODULES
2020
#include <mutex>
2121
#include "async_simple/coro/Lazy.h"
22+
#include "async_simple/coro/SpinLock.h"
2223

2324
#endif // ASYNC_SIMPLE_USE_MODULES
2425

@@ -50,6 +51,7 @@ class ConditionVariable {
5051
private:
5152
friend class ConditionVariableAwaiter<Lock>;
5253
std::atomic<ConditionVariableAwaiter<Lock>*> _awaiters = nullptr;
54+
SpinLock _notifyLock;
5355
};
5456

5557
template <class Lock>
@@ -94,21 +96,31 @@ inline Lazy<> ConditionVariable<Lock>::wait(Lock& lock, Pred&& pred) noexcept {
9496

9597
template <class Lock>
9698
inline void ConditionVariable<Lock>::notifyAll() noexcept {
97-
auto awaitings = _awaiters.load(std::memory_order_acquire);
98-
while (!_awaiters.compare_exchange_weak(awaitings, nullptr,
99-
std::memory_order_acq_rel,
100-
std::memory_order_acquire))
101-
;
99+
ConditionVariableAwaiter<Lock>* awaitings = nullptr;
100+
{
101+
ScopedSpinLock notifyLock(_notifyLock);
102+
awaitings = _awaiters.load(std::memory_order_acquire);
103+
while (!_awaiters.compare_exchange_weak(awaitings, nullptr,
104+
std::memory_order_acq_rel,
105+
std::memory_order_acquire))
106+
;
107+
}
102108
resumeWaiters(awaitings);
103109
}
104110

105111
template <class Lock>
106112
inline void ConditionVariable<Lock>::notifyOne() noexcept {
107-
auto awaitings = _awaiters.load(std::memory_order_acquire);
108-
while (awaitings && !_awaiters.compare_exchange_weak(awaitings, awaitings->_next,
109-
std::memory_order_acq_rel,
110-
std::memory_order_acquire))
111-
;
113+
ConditionVariableAwaiter<Lock>* awaitings = nullptr;
114+
{
115+
ScopedSpinLock notifyLock(_notifyLock);
116+
awaitings = _awaiters.load(std::memory_order_acquire);
117+
while (awaitings &&
118+
!_awaiters.compare_exchange_weak(awaitings, awaitings->_next,
119+
std::memory_order_acq_rel,
120+
std::memory_order_acquire))
121+
;
122+
}
123+
112124
if (!awaitings) {
113125
return;
114126
}

0 commit comments

Comments
 (0)