Fix segfault in EOWNERDEAD handling - remove incorrect ref count manipulation

Root cause: The previous code incorrectly called shm_->sub_ref() when handling
EOWNERDEAD, which could cause the shared memory to be freed prematurely while
the mutex pointer was still in use, leading to segmentation fault.

Fix: Remove the shm_->sub_ref() call. When EOWNERDEAD is returned, it means
we have successfully acquired the lock. We only need to call pthread_mutex_consistent()
to make the mutex usable again, then return success. The shared memory reference
count should not be modified in this path.

This fixes the segfault in MutexTest.TryLockExceptionSafety on FreeBSD 15.
This commit is contained in:
木头云 2025-12-06 06:21:38 +00:00
parent a82e3faf63
commit 47fa303455

View File

@ -206,19 +206,15 @@ public:
case ETIMEDOUT: case ETIMEDOUT:
return false; return false;
case EOWNERDEAD: { case EOWNERDEAD: {
if (shm_->ref() > 1) { // EOWNERDEAD means we have successfully acquired the lock,
shm_->sub_ref(); // but the previous owner died. We need to make it consistent.
}
int eno2 = ::pthread_mutex_consistent(mutex_); int eno2 = ::pthread_mutex_consistent(mutex_);
if (eno2 != 0) { if (eno2 != 0) {
ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
return false; return false;
} }
// EOWNERDEAD means we have successfully acquired the lock, // After calling pthread_mutex_consistent(), the mutex is now in a
// but the previous owner died. After calling pthread_mutex_consistent(), // consistent state and we hold the lock. Return success.
// the mutex is now in a consistent state and we hold the lock.
// We should return success here, not unlock and retry.
// This avoids issues with FreeBSD's robust mutex list management.
return true; return true;
} }
default: default:
@ -238,15 +234,15 @@ public:
case ETIMEDOUT: case ETIMEDOUT:
return false; return false;
case EOWNERDEAD: { case EOWNERDEAD: {
if (shm_->ref() > 1) { // EOWNERDEAD means we have successfully acquired the lock,
shm_->sub_ref(); // but the previous owner died. We need to make it consistent.
}
int eno2 = ::pthread_mutex_consistent(mutex_); int eno2 = ::pthread_mutex_consistent(mutex_);
if (eno2 != 0) { if (eno2 != 0) {
ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
throw std::system_error{eno2, std::system_category()}; throw std::system_error{eno2, std::system_category()};
} }
// Successfully acquired the lock after making it consistent // After calling pthread_mutex_consistent(), the mutex is now in a
// consistent state and we hold the lock. Return success.
return true; return true;
} }
default: default: