When I added the knote_acquire() call in kqueue_register(), I overlooked
the fact that the knote could be modified by a (soft) interrupt.
Interrupts have to be blocked when changing kn_status. Otherwise the
state could become confused.

The diff below adds splhigh() where kn_status is modified. This also
ensures that the steps of knote_activate() run as an uninterrupted
sequence. As a consequence, knote_enqueue() and knote_dequeue() can rely
on the caller to raise the spl.

OK?

Index: kern/kern_event.c
===================================================================
RCS file: src/sys/kern/kern_event.c,v
retrieving revision 1.124
diff -u -p -r1.124 kern_event.c
--- kern/kern_event.c   14 Feb 2020 16:50:25 -0000      1.124
+++ kern/kern_event.c   15 Feb 2020 16:52:41 -0000
@@ -312,8 +312,11 @@ filt_proc(struct knote *kn, long hint)
         */
        if (event == NOTE_EXIT) {
                struct process *pr = kn->kn_ptr.p_process;
+               int s;
 
+               s = splhigh();
                kn->kn_status |= KN_DETACHED;
+               splx(s);
                kn->kn_flags |= (EV_EOF | EV_ONESHOT);
                kn->kn_data = W_EXITCODE(pr->ps_xexit, pr->ps_xsig);
                SLIST_REMOVE(&pr->ps_klist, kn, knote, kn_selnext);
@@ -712,13 +715,16 @@ again:
                SLIST_FOREACH(kn, list, kn_link) {
                        if (kev->filter == kn->kn_filter &&
                            kev->ident == kn->kn_id) {
-                               if (knote_acquire(kn) == 0) {
+                               s = splhigh();
+                               if (!knote_acquire(kn)) {
+                                       splx(s);
                                        if (fp != NULL) {
                                                FRELE(fp, p);
                                                fp = NULL;
                                        }
                                        goto again;
                                }
+                               splx(s);
                                break;
                        }
                }
@@ -820,7 +826,9 @@ again:
                splx(s);
        }
 
+       s = splhigh();
        knote_release(kn);
+       splx(s);
 done:
        if (fp != NULL)
                FRELE(fp, p);
@@ -1157,6 +1165,7 @@ kqueue_expand_list(struct kqueue *kq, in
 int
 knote_acquire(struct knote *kn)
 {
+       splassert(IPL_HIGH);
        KASSERT(kn->kn_filter != EVFILT_MARKER);
 
        if (kn->kn_status & KN_PROCESSING) {
@@ -1175,6 +1184,7 @@ knote_acquire(struct knote *kn)
 void
 knote_release(struct knote *kn)
 {
+       splassert(IPL_HIGH);
        KASSERT(kn->kn_filter != EVFILT_MARKER);
        KASSERT(kn->kn_status & KN_PROCESSING);
 
@@ -1192,9 +1202,13 @@ knote_release(struct knote *kn)
 void
 knote_activate(struct knote *kn)
 {
+       int s;
+
+       s = splhigh();
        kn->kn_status |= KN_ACTIVE;
        if ((kn->kn_status & (KN_QUEUED | KN_DISABLED)) == 0)
                knote_enqueue(kn);
+       splx(s);
 }
 
 /*
@@ -1217,10 +1231,15 @@ void
 knote_remove(struct proc *p, struct klist *list)
 {
        struct knote *kn;
+       int s;
 
        while ((kn = SLIST_FIRST(list)) != NULL) {
-               if (!knote_acquire(kn))
+               s = splhigh();
+               if (!knote_acquire(kn)) {
+                       splx(s);
                        continue;
+               }
+               splx(s);
                kn->kn_fop->f_detach(kn);
                knote_drop(kn, p);
        }
@@ -1297,6 +1316,7 @@ knote_drop(struct knote *kn, struct proc
 {
        struct kqueue *kq = kn->kn_kq;
        struct klist *list;
+       int s;
 
        KASSERT(kn->kn_filter != EVFILT_MARKER);
 
@@ -1306,12 +1326,14 @@ knote_drop(struct knote *kn, struct proc
                list = &kq->kq_knhash[KN_HASH(kn->kn_id, kq->kq_knhashmask)];
 
        SLIST_REMOVE(list, kn, knote, kn_link);
+       s = splhigh();
        if (kn->kn_status & KN_QUEUED)
                knote_dequeue(kn);
        if (kn->kn_status & KN_WAITING) {
                kn->kn_status &= ~KN_WAITING;
                wakeup(kn);
        }
+       splx(s);
        if (kn->kn_fop->f_isfd)
                FRELE(kn->kn_fp, p);
        pool_put(&knote_pool, kn);
@@ -1322,8 +1344,8 @@ void
 knote_enqueue(struct knote *kn)
 {
        struct kqueue *kq = kn->kn_kq;
-       int s = splhigh();
 
+       splassert(IPL_HIGH);
        KASSERT(kn->kn_filter != EVFILT_MARKER);
        KASSERT((kn->kn_status & KN_QUEUED) == 0);
 
@@ -1332,7 +1354,6 @@ knote_enqueue(struct knote *kn)
        kn->kn_status |= KN_QUEUED;
        kq->kq_count++;
        kqueue_check(kq);
-       splx(s);
        kqueue_wakeup(kq);
 }
 
@@ -1340,8 +1361,8 @@ void
 knote_dequeue(struct knote *kn)
 {
        struct kqueue *kq = kn->kn_kq;
-       int s = splhigh();
 
+       splassert(IPL_HIGH);
        KASSERT(kn->kn_filter != EVFILT_MARKER);
        KASSERT(kn->kn_status & KN_QUEUED);
 
@@ -1350,13 +1371,13 @@ knote_dequeue(struct knote *kn)
        kn->kn_status &= ~KN_QUEUED;
        kq->kq_count--;
        kqueue_check(kq);
-       splx(s);
 }
 
 void
 klist_invalidate(struct klist *list)
 {
        struct knote *kn;
+       int s;
 
        /*
         * NET_LOCK() must not be held because it can block another thread
@@ -1364,12 +1385,16 @@ klist_invalidate(struct klist *list)
         */
        NET_ASSERT_UNLOCKED();
 
+       s = splhigh();
        while ((kn = SLIST_FIRST(list)) != NULL) {
-               if (knote_acquire(kn) == 0)
+               if (!knote_acquire(kn))
                        continue;
+               splx(s);
                kn->kn_fop->f_detach(kn);
                kn->kn_fop = &dead_filtops;
                knote_activate(kn);
+               s = splhigh();
                knote_release(kn);
        }
+       splx(s);
 }

Reply via email to