kqueue: Fix termination assert
authorvisa <visa@openbsd.org>
Wed, 27 Jan 2021 02:58:03 +0000 (02:58 +0000)
committervisa <visa@openbsd.org>
Wed, 27 Jan 2021 02:58:03 +0000 (02:58 +0000)
When a kqueue file is closed, the kqueue can still have threads
scanning it. Consequently, kqueue_terminate() can see scan markers
in the event queue. These markers are removed when the scanning threads
leave the kqueue. Take this into account when checking the queue's
state, to avoid a panic when kqueue is closed from under a thread.

OK anton@

Reported-by: syzbot+757c60a2aa1125137cce@syzkaller.appspotmail.com
sys/kern/kern_event.c

index c5d47d3..5471b29 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_event.c,v 1.159 2021/01/17 05:56:32 visa Exp $   */
+/*     $OpenBSD: kern_event.c,v 1.160 2021/01/27 02:58:03 visa Exp $   */
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -206,6 +206,8 @@ KQRELE(struct kqueue *kq)
                fdpunlock(fdp);
        }
 
+       KASSERT(TAILQ_EMPTY(&kq->kq_head));
+
        free(kq->kq_knlist, M_KEVENT, kq->kq_knlistsize *
            sizeof(struct knlist));
        hashfree(kq->kq_knhash, KN_HASHSIZE, M_KEVENT);
@@ -1265,7 +1267,15 @@ kqueue_purge(struct proc *p, struct kqueue *kq)
 void
 kqueue_terminate(struct proc *p, struct kqueue *kq)
 {
-       KASSERT(TAILQ_EMPTY(&kq->kq_head));
+       struct knote *kn;
+
+       /*
+        * Any remaining entries should be scan markers.
+        * They are removed when the ongoing scans finish.
+        */
+       KASSERT(kq->kq_count == 0);
+       TAILQ_FOREACH(kn, &kq->kq_head, kn_tqe)
+               KASSERT(kn->kn_filter == EVFILT_MARKER);
 
        kq->kq_state |= KQ_DYING;
        kqueue_wakeup(kq);