summaryrefslogtreecommitdiff
path: root/absl/synchronization/internal/create_thread_identity.cc
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2022-05-17 01:44:42 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2022-05-17 01:45:38 -0700
commit9444b11e0c4e1f079c87067b5bbab1c5ff718809 (patch)
treed533400407231d6e74c0f21883b4e6a9dea033ee /absl/synchronization/internal/create_thread_identity.cc
parentaac2279f22eef04d01fc42e66fc183a32f08a9b4 (diff)
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/create_thread_identity.cc')
-rw-r--r--absl/synchronization/internal/create_thread_identity.cc15
1 files changed, 9 insertions, 6 deletions
diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc
index 53a71b34..44e6129b 100644
--- a/absl/synchronization/internal/create_thread_identity.cc
+++ b/absl/synchronization/internal/create_thread_identity.cc
@@ -38,7 +38,7 @@ ABSL_CONST_INIT static base_internal::ThreadIdentity* thread_identity_freelist;
// A per-thread destructor for reclaiming associated ThreadIdentity objects.
// Since we must preserve their storage we cache them for re-use.
-void ReclaimThreadIdentity(void* v) {
+static void ReclaimThreadIdentity(void* v) {
base_internal::ThreadIdentity* identity =
static_cast<base_internal::ThreadIdentity*>(v);
@@ -48,8 +48,6 @@ void ReclaimThreadIdentity(void* v) {
base_internal::LowLevelAlloc::Free(identity->per_thread_synch.all_locks);
}
- PerThreadSem::Destroy(identity);
-
// We must explicitly clear the current thread's identity:
// (a) Subsequent (unrelated) per-thread destructors may require an identity.
// We must guarantee a new identity is used in this case (this instructor
@@ -71,7 +69,12 @@ static intptr_t RoundUp(intptr_t addr, intptr_t align) {
return (addr + align - 1) & ~(align - 1);
}
-static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) {
+void OneTimeInitThreadIdentity(base_internal::ThreadIdentity* identity) {
+ PerThreadSem::Init(identity);
+}
+
+static void ResetThreadIdentityBetweenReuse(
+ base_internal::ThreadIdentity* identity) {
base_internal::PerThreadSynch* pts = &identity->per_thread_synch;
pts->next = nullptr;
pts->skip = nullptr;
@@ -116,8 +119,9 @@ static base_internal::ThreadIdentity* NewThreadIdentity() {
identity = reinterpret_cast<base_internal::ThreadIdentity*>(
RoundUp(reinterpret_cast<intptr_t>(allocation),
base_internal::PerThreadSynch::kAlignment));
+ OneTimeInitThreadIdentity(identity);
}
- ResetThreadIdentity(identity);
+ ResetThreadIdentityBetweenReuse(identity);
return identity;
}
@@ -127,7 +131,6 @@ static base_internal::ThreadIdentity* NewThreadIdentity() {
// REQUIRES: CurrentThreadIdentity(false) == nullptr
base_internal::ThreadIdentity* CreateThreadIdentity() {
base_internal::ThreadIdentity* identity = NewThreadIdentity();
- PerThreadSem::Init(identity);
// Associate the value with the current thread, and attach our destructor.
base_internal::SetCurrentThreadIdentity(identity, ReclaimThreadIdentity);
return identity;