aboutsummaryrefslogtreecommitdiff
path: root/absl/synchronization/internal/waiter.cc
diff options
context:
space:
mode:
authorAbseil Team <absl-team@google.com>2022-05-17 01:44:42 -0700
committerCopybara-Service <copybara-worker@google.com>2022-05-17 01:45:38 -0700
commit9444b11e0c4e1f079c87067b5bbab1c5ff718809 (patch)
treed533400407231d6e74c0f21883b4e6a9dea033ee /absl/synchronization/internal/waiter.cc
parentaac2279f22eef04d01fc42e66fc183a32f08a9b4 (diff)
downloadabseil-9444b11e0c4e1f079c87067b5bbab1c5ff718809.tar.gz
abseil-9444b11e0c4e1f079c87067b5bbab1c5ff718809.tar.bz2
abseil-9444b11e0c4e1f079c87067b5bbab1c5ff718809.zip
absl: fix use-after-free in Mutex/CondVar
Both Mutex and CondVar signal PerThreadSem/Waiter after satisfying the wait condition, as the result the waiting thread may return w/o waiting on the PerThreadSem/Waiter at all. If the waiting thread then exits, it currently destroys Waiter object. As the result Waiter::Post can be called on already destroyed object. PerThreadSem/Waiter must be type-stable after creation and must not be destroyed. The futex-based implementation is the only one that is not affected by the bug since there is effectively nothing to destroy (maybe only UBSan/ASan could complain about calling methods on a destroyed object). Here is the problematic sequence of events: 1: void Mutex::Block(PerThreadSynch *s) { 2: while (s->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) { 3: if (!DecrementSynchSem(this, s, s->waitp->timeout)) { 4: PerThreadSynch *Mutex::Wakeup(PerThreadSynch *w) { 5: ... 6: w->state.store(PerThreadSynch::kAvailable, std::memory_order_release); 7: IncrementSynchSem(this, w); 8: ... 9: } Consider line 6 is executed, then line 2 observes kAvailable and line 3 is not called. The thread executing Mutex::Block returns from the method, acquires the mutex, releases the mutex, exits and destroys PerThreadSem/Waiter. Now Mutex::Wakeup resumes and executes line 7 on the destroyed object. Boom! CondVar uses a similar pattern. Moreover the semaphore-based Waiter implementation is not even destruction-safe (the Waiter cannot be used to signal own destruction). So even if Mutex/CondVar would always pair Waiter::Post with Waiter::Wait before destroying PerThreadSem/Waiter, it would still be subject to use-after-free bug on the semaphore. PiperOrigin-RevId: 449159939 Change-Id: I497134fa8b6ce1294a422827c5f0de0e897cea31
Diffstat (limited to 'absl/synchronization/internal/waiter.cc')
-rw-r--r--absl/synchronization/internal/waiter.cc25
1 files changed, 0 insertions, 25 deletions
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
index 28ef311e..f2051d67 100644
--- a/absl/synchronization/internal/waiter.cc
+++ b/absl/synchronization/internal/waiter.cc
@@ -71,8 +71,6 @@ Waiter::Waiter() {
futex_.store(0, std::memory_order_relaxed);
}
-Waiter::~Waiter() = default;
-
bool Waiter::Wait(KernelTimeout t) {
// Loop until we can atomically decrement futex from a positive
// value, waiting on a futex while we believe it is zero.
@@ -161,18 +159,6 @@ Waiter::Waiter() {
wakeup_count_ = 0;
}
-Waiter::~Waiter() {
- const int err = pthread_mutex_destroy(&mu_);
- if (err != 0) {
- ABSL_RAW_LOG(FATAL, "pthread_mutex_destroy failed: %d", err);
- }
-
- const int err2 = pthread_cond_destroy(&cv_);
- if (err2 != 0) {
- ABSL_RAW_LOG(FATAL, "pthread_cond_destroy failed: %d", err2);
- }
-}
-
bool Waiter::Wait(KernelTimeout t) {
struct timespec abs_timeout;
if (t.has_timeout()) {
@@ -240,12 +226,6 @@ Waiter::Waiter() {
wakeups_.store(0, std::memory_order_relaxed);
}
-Waiter::~Waiter() {
- if (sem_destroy(&sem_) != 0) {
- ABSL_RAW_LOG(FATAL, "sem_destroy failed with errno %d\n", errno);
- }
-}
-
bool Waiter::Wait(KernelTimeout t) {
struct timespec abs_timeout;
if (t.has_timeout()) {
@@ -363,11 +343,6 @@ Waiter::Waiter() {
wakeup_count_ = 0;
}
-// SRW locks and condition variables do not need to be explicitly destroyed.
-// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
-// https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with
-Waiter::~Waiter() = default;
-
bool Waiter::Wait(KernelTimeout t) {
SRWLOCK *mu = WinHelper::GetLock(this);
CONDITION_VARIABLE *cv = WinHelper::GetCond(this);