user_mutex: Fix potential race in switch_lock.
We need to set the "to" mutex as locked+waiting before performing the unlock of the first mutex, otherwise something in userland could unset the "locked" flag but never call the kernel because "waiting" had not yet been set. In practice, the one consumer of this API (pthread_cond) could not, at present, wind up in that situation, as far as I can tell, so this race was entirely theoretical.
This commit is contained in:
@@ -176,9 +176,8 @@ user_mutex_wait_locked(int32* mutex, phys_addr_t physicalAddress, const char* na
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
user_mutex_lock_locked(int32* mutex, phys_addr_t physicalAddress,
|
||||
const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
|
||||
static bool
|
||||
user_mutex_prepare_to_lock(int32* mutex)
|
||||
{
|
||||
int32 oldValue = user_atomic_or(mutex,
|
||||
B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING);
|
||||
@@ -187,9 +186,20 @@ user_mutex_lock_locked(int32* mutex, phys_addr_t physicalAddress,
|
||||
// clear the waiting flag and be done
|
||||
if ((oldValue & B_USER_MUTEX_WAITING) == 0)
|
||||
user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
|
||||
return B_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
user_mutex_lock_locked(int32* mutex, phys_addr_t physicalAddress,
|
||||
const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
|
||||
{
|
||||
if (user_mutex_prepare_to_lock(mutex))
|
||||
return B_OK;
|
||||
|
||||
bool lastWaiter;
|
||||
status_t error = user_mutex_wait_locked(mutex, physicalAddress, name,
|
||||
flags, timeout, locker, lastWaiter);
|
||||
@@ -338,12 +348,16 @@ user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
|
||||
// unlock the first mutex and lock the second one
|
||||
{
|
||||
MutexLocker locker(sUserMutexTableLock);
|
||||
|
||||
const bool alreadyLocked = user_mutex_prepare_to_lock(toMutex);
|
||||
user_atomic_and(fromMutex, ~(int32)B_USER_MUTEX_LOCKED);
|
||||
user_mutex_unblock_locked(fromMutex, fromWiringInfo.physicalAddress,
|
||||
flags);
|
||||
|
||||
error = user_mutex_lock_locked(toMutex, toWiringInfo.physicalAddress,
|
||||
name, flags, timeout, locker);
|
||||
if (!alreadyLocked) {
|
||||
error = user_mutex_lock_locked(toMutex, toWiringInfo.physicalAddress,
|
||||
name, flags, timeout, locker);
|
||||
}
|
||||
}
|
||||
|
||||
// unwire the pages
|
||||
|
||||
Reference in New Issue
Block a user