kernel/condition_variable: Acquire thread->scheduler_lock before unsetting fVariable.

This fixes a race condition as described in the inline comment.

Hopefully fixes #17444.
This commit is contained in:
Augustin Cavalier 2021-12-06 13:11:28 -05:00
parent 949005a59b
commit 858210daf4

View File

@ -400,6 +400,10 @@ ConditionVariable::_NotifyLocked(bool all, status_t result)
} else { } else {
const status_t waitStatus = atomic_get_and_set(&entry->fWaitStatus, result); const status_t waitStatus = atomic_get_and_set(&entry->fWaitStatus, result);
// Prevent the thread from changing status after we unset its fVariable,
// as otherwise it could re-block itself before we call thread_unblock.
SpinLocker threadLocker(thread->scheduler_lock);
// No matter what the thread is doing, as we were the ones to clear its // No matter what the thread is doing, as we were the ones to clear its
// fThread, so we are the ones responsible for decrementing fEntriesCount. // fThread, so we are the ones responsible for decrementing fEntriesCount.
// (We may not validly access the entry once we unset its fVariable.) // (We may not validly access the entry once we unset its fVariable.)
@ -409,7 +413,7 @@ ConditionVariable::_NotifyLocked(bool all, status_t result)
// Do this after unsetting fVariable, as in case the entry wakes up // Do this after unsetting fVariable, as in case the entry wakes up
// and tries to remove itself, it need not not have to wait for us. // and tries to remove itself, it need not not have to wait for us.
if (waitStatus == STATUS_WAITING) if (waitStatus == STATUS_WAITING)
thread_unblock(thread, result); thread_unblock_locked(thread, result);
} }
if (!all) if (!all)