mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
Fix FreeBSD segfault by unlocking mutex before destruction
Root cause: FreeBSD's robust mutex implementation (libthr) maintains a per-thread robust list of mutexes. The error 'rb error 14' (EFAULT - Bad address) indicates that the robust list contained a dangling pointer to a destroyed mutex. When a mutex object is destroyed (via close() or clear()), if the mutex is still in the current thread's robust list, FreeBSD's libthr may try to access it later and encounter an invalid pointer, causing a segmentation fault. This happened in MutexTest.TryLockExceptionSafety because: 1. The test called try_lock() which successfully acquired the lock 2. The test ended without calling unlock() 3. The mutex destructor called close() 4. close() called pthread_mutex_destroy() on a mutex that was: - Still locked by the current thread, OR - Still in the thread's robust list Solution: Call pthread_mutex_unlock() before pthread_mutex_destroy() in both close() and clear() methods. This ensures: 1. The mutex is unlocked if we hold the lock 2. The mutex is removed from the thread's robust list 3. Subsequent pthread_mutex_destroy() is safe We ignore errors from pthread_mutex_unlock() because: - If we don't hold the lock, EPERM is expected and harmless - If the mutex is already unlocked, this is a no-op - Even if there's an error, we still want to proceed with cleanup This fix is platform-agnostic and should not affect Linux/QNX behavior, as both also use pthread robust mutexes with similar semantics. Fixes the segfault in MutexTest.TryLockExceptionSafety on FreeBSD 15.
This commit is contained in:
parent
47fa303455
commit
5517f48681
@ -150,6 +150,17 @@ public:
|
|||||||
|
|
||||||
void close() noexcept {
|
void close() noexcept {
|
||||||
if ((ref_ != nullptr) && (shm_ != nullptr) && (mutex_ != nullptr)) {
|
if ((ref_ != nullptr) && (shm_ != nullptr) && (mutex_ != nullptr)) {
|
||||||
|
// Try to unlock the mutex before destroying it.
|
||||||
|
// This is important for robust mutexes on FreeBSD, which maintain
|
||||||
|
// a per-thread robust list. If we destroy a mutex while it's in
|
||||||
|
// the robust list (even if not locked), FreeBSD may encounter
|
||||||
|
// dangling pointers later, leading to segfaults.
|
||||||
|
// We ignore any errors from unlock() since:
|
||||||
|
// 1. If we don't hold the lock, EPERM is expected and harmless
|
||||||
|
// 2. If the mutex is already unlocked, this is a no-op
|
||||||
|
// 3. If there's an error, we still want to proceed with cleanup
|
||||||
|
::pthread_mutex_unlock(mutex_);
|
||||||
|
|
||||||
if (shm_->name() != nullptr) {
|
if (shm_->name() != nullptr) {
|
||||||
release_mutex(shm_->name(), [this] {
|
release_mutex(shm_->name(), [this] {
|
||||||
auto self_ref = ref_->fetch_sub(1, std::memory_order_relaxed);
|
auto self_ref = ref_->fetch_sub(1, std::memory_order_relaxed);
|
||||||
@ -171,6 +182,9 @@ public:
|
|||||||
|
|
||||||
void clear() noexcept {
|
void clear() noexcept {
|
||||||
if ((shm_ != nullptr) && (mutex_ != nullptr)) {
|
if ((shm_ != nullptr) && (mutex_ != nullptr)) {
|
||||||
|
// Try to unlock before destroying, same reasoning as in close()
|
||||||
|
::pthread_mutex_unlock(mutex_);
|
||||||
|
|
||||||
if (shm_->name() != nullptr) {
|
if (shm_->name() != nullptr) {
|
||||||
release_mutex(shm_->name(), [this] {
|
release_mutex(shm_->name(), [this] {
|
||||||
int eno;
|
int eno;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user