sem_post(): wake up the sleeper only after adjusting has_waiters

If the caller of sem_post() wakes up a thread sleeping via sem_wait()
before it clears the has_waiters flag, the caller of sem_wait() has no way of
knowing when it is safe to destroy the semaphore and reuse the memory. This is
because the caller of sem_post() may be interrupted between the wake step and
the clearing of has_waiters. It will then write into the has_waiters flag in
userspace after being preempted for some unknown amount of time.

Reviewed by:	jhb, kib, vangyzen
Approved by:	kib (mentor), vangyzen (mentor)
MFC after:	2 weeks
Sponsored by:	Dell Inc.
Differential Revision:	https://reviews.freebsd.org/D7505
This commit is contained in:
Eric Badger 2016-08-15 20:09:09 +00:00
parent ed12504ac7
commit b0f2185bbe
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=304184

View File

@ -3123,7 +3123,6 @@ do_sem_wake(struct thread *td, struct _usem *sem)
umtxq_busy(&key); umtxq_busy(&key);
cnt = umtxq_count(&key); cnt = umtxq_count(&key);
if (cnt > 0) { if (cnt > 0) {
umtxq_signal(&key, 1);
/* /*
* Check if count is greater than 0, this means the memory is * Check if count is greater than 0, this means the memory is
* still being referenced by user code, so we can safely * still being referenced by user code, so we can safely
@ -3136,6 +3135,7 @@ do_sem_wake(struct thread *td, struct _usem *sem)
if (error == -1) if (error == -1)
error = EFAULT; error = EFAULT;
} }
umtxq_signal(&key, 1);
} }
umtxq_unbusy(&key); umtxq_unbusy(&key);
umtxq_unlock(&key); umtxq_unlock(&key);
@ -3235,8 +3235,6 @@ do_sem2_wake(struct thread *td, struct _usem2 *sem)
umtxq_busy(&key); umtxq_busy(&key);
cnt = umtxq_count(&key); cnt = umtxq_count(&key);
if (cnt > 0) { if (cnt > 0) {
umtxq_signal(&key, 1);
/* /*
* If this was the last sleeping thread, clear the waiters * If this was the last sleeping thread, clear the waiters
* flag in _count. * flag in _count.
@ -3251,6 +3249,8 @@ do_sem2_wake(struct thread *td, struct _usem2 *sem)
error = EFAULT; error = EFAULT;
umtxq_lock(&key); umtxq_lock(&key);
} }
umtxq_signal(&key, 1);
} }
umtxq_unbusy(&key); umtxq_unbusy(&key);
umtxq_unlock(&key); umtxq_unlock(&key);