Fix race between sig_kill and _sleepq_wait
@@ -213,24 +213,42 @@ static void sq_leave(thread_t *td, sleepq_chain_t *sc, sleepq_t *sq) {
}
}
+static inline bool _sleepq_interrupted_early(thread_t *td, sleep_t sleep) {
+ return (td->td_flags & TDF_NEEDSIGCHK) != 0 && sleep == SLP_INTR;
+}
+
static int _sleepq_wait(void *wchan, const void *waitpt, sleep_t sleep) {
thread_t *td = thread_self();
int status = 0;
+ /* If there are pending signals, interrupt the sleep immediately. */
+ WITH_SPIN_LOCK (&td->td_spin) {
+ if (_sleepq_interrupted_early(td, sleep))
+ return EINTR;
+ }
+
if (waitpt == NULL)
waitpt = __caller(0);
sq_enter(td, wchan, waitpt, sleep);
/* The code can be interrupted in here.
- * A race is avoided by clever use of TDF_SLEEPY flag. */
+ * A race is avoided by clever use of TDF_SLEEPY flag.
+ * We can also get a signal in here -- interrupt early if we got one.
+ * The first signal check is an optimization that saves us the call
+ * to sq_enter. */
WITH_SPIN_LOCK (&td->td_spin) {
if (td->td_flags & TDF_SLEEPY) {
td->td_flags &= ~TDF_SLEEPY;
+ if (_sleepq_interrupted_early(td, sleep)) {
+ td->td_flags &= ~(TDF_SLPINTR | TDF_SLPTIMED);
+ status = EINTR;
+ } else {
td->td_state = TDS_SLEEPING;
sched_switch();
}
+ }
/* After wakeup, only one of the following flags may be set:
* - TDF_SLPINTR if sleep was aborted,
* - TDF_SLPTIMED if sleep has timed out. */
