kernel - Fix kqueue race with NOTE_EXIT
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 8 Dec 2011 02:34:01 +0000 (18:34 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 8 Dec 2011 02:34:01 +0000 (18:34 -0800)
* Fix a kqueue race where the process receiving a NOTE_EXIT can go
  away while the NOTE_EXIT is being delivered.

* Add a required PHOLD/PRELE since knote_remove() can block.

* Properly return on-zero if KN_DETACHED is set at the end of knote_release()
  so the caller knows to restart the list scan.

Reported-by: sephe
sys/kern/kern_event.c

index 5583d1b..ba553f3 100644 (file)
@@ -296,10 +296,12 @@ filt_proc(struct knote *kn, long hint)
        if (event == NOTE_EXIT) {
                struct proc *p = kn->kn_ptr.p_proc;
                if ((kn->kn_status & KN_DETACHED) == 0) {
+                       PHOLD(p);
                        knote_remove(&p->p_klist, kn);
                        kn->kn_status |= KN_DETACHED;
                        kn->kn_data = p->p_xstat;
                        kn->kn_ptr.p_proc = NULL;
+                       PRELE(p);
                }
                kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT); 
                return (1);
@@ -444,7 +446,7 @@ knote_acquire(struct knote *kn)
  *
  * Caller must be holding the related kq token
  *
- * Non-zero is returned if the knote is destroyed.
+ * Non-zero is returned if the knote is destroyed or detached.
  */
 static __inline
 int
@@ -464,8 +466,13 @@ knote_release(struct knote *kn)
                if (filter_event(kn, 0))
                        KNOTE_ACTIVATE(kn);
        }
-       kn->kn_status &= ~KN_PROCESSING;
-       return(0);
+       if (kn->kn_status & KN_DETACHED) {
+               kn->kn_status &= ~KN_PROCESSING;
+               return(1);
+       } else {
+               kn->kn_status &= ~KN_PROCESSING;
+               return(0);
+       }
 }
 
 /*