kqueue: Invalidate revoked vnodes' knotes on the fly
authorvisa <visa@openbsd.org>
Sat, 25 Dec 2021 11:04:58 +0000 (11:04 +0000)
committervisa <visa@openbsd.org>
Sat, 25 Dec 2021 11:04:58 +0000 (11:04 +0000)
When a tty device is revoked, the associated knotes should be
invalidated. Otherwise the user processes can keep on receiving
events from the device.

It appears tricky to do the invalidation as part of revocation
in a way that does not allow unwanted event registration or clutter
the tty code. For now, make the knotes invalid lazily before delivery.

OK mpi@

sys/kern/kern_event.c

index 109f789..33f30f2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_event.c,v 1.177 2021/12/20 16:24:32 visa Exp $   */
+/*     $OpenBSD: kern_event.c,v 1.178 2021/12/25 11:04:58 visa Exp $   */
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -55,6 +55,7 @@
 #include <sys/syscallargs.h>
 #include <sys/time.h>
 #include <sys/timeout.h>
+#include <sys/vnode.h>
 #include <sys/wait.h>
 
 #ifdef DIAGNOSTIC
@@ -1381,6 +1382,36 @@ retry:
                        continue;
                }
 
+               /*
+                * Invalidate knotes whose vnodes have been revoked.
+                * This is a workaround; it is tricky to clear existing
+                * knotes and prevent new ones from being registered
+                * with the current revocation mechanism.
+                */
+               if ((kn->kn_fop->f_flags & FILTEROP_ISFD) &&
+                   kn->kn_fp != NULL &&
+                   kn->kn_fp->f_type == DTYPE_VNODE) {
+                       struct vnode *vp = kn->kn_fp->f_data;
+
+                       if (__predict_false(vp->v_op == &dead_vops &&
+                           kn->kn_fop != &dead_filtops)) {
+                               filter_detach(kn);
+                               kn->kn_fop = &dead_filtops;
+
+                               /*
+                                * Check if the event should be delivered.
+                                * Use f_event directly because this is
+                                * a special situation.
+                                */
+                               if (kn->kn_fop->f_event(kn, 0) == 0) {
+                                       filter_detach(kn);
+                                       knote_drop(kn, p);
+                                       mtx_enter(&kq->kq_lock);
+                                       continue;
+                               }
+                       }
+               }
+
                memset(kevp, 0, sizeof(*kevp));
                if (filter_process(kn, kevp) == 0) {
                        mtx_enter(&kq->kq_lock);