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))