kernel - Fix system lockup w/ kqueue based select/poll due to dup2()
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 21 Aug 2010 21:00:49 +0000 (14:00 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 21 Aug 2010 21:00:49 +0000 (14:00 -0700)
* dup2() was not calling knote_fdclose() on the original descriptor being
  replaced

* fdfree() (called by exit) was not cleaning out knotes either, though
  exit*() probably took care of it already when it closed the descriptors.

* Assert that a file has no knotes on the last fdrop().

Reported-by: everyone + lentferj + alexh + swildner + Studbolt
sys/kern/kern_descrip.c
sys/kern/kern_event.c

index 791e974..5941b6b 100644 (file)
@@ -622,6 +622,11 @@ retry:
         * close() were performed on it).
         */
        if (delfp) {
+               if (SLIST_FIRST(&delfp->f_klist)) {
+                       get_mplock();
+                       knote_fdclose(delfp, fdp, new);
+                       rel_mplock();
+               }
                closef(delfp, p);
                if (holdleaders) {
                        spin_lock_wr(&fdp->fd_spin);
@@ -1993,6 +1998,11 @@ fdfree(struct proc *p, struct filedesc *repl)
                        fp = funsetfd_locked(fdp, i);
                        if (fp) {
                                spin_unlock_wr(&fdp->fd_spin);
+                               if (SLIST_FIRST(&fp->f_klist)) {
+                                       get_mplock();
+                                       knote_fdclose(fp, fdp, i);
+                                       rel_mplock();
+                               }
                                closef(fp, p);
                                spin_lock_wr(&fdp->fd_spin);
                        }
@@ -2390,6 +2400,7 @@ fdrop(struct file *fp)
        if (atomic_fetchadd_int(&fp->f_count, -1) > 1)
                return (0);
 
+       KKASSERT(SLIST_FIRST(&fp->f_klist) == NULL);
        get_mplock();
 
        /*
index 65ff246..bef6ec7 100644 (file)
@@ -133,6 +133,9 @@ static int          kq_ncallouts = 0;
 static int             kq_calloutmax = (4 * 1024);
 SYSCTL_INT(_kern, OID_AUTO, kq_calloutmax, CTLFLAG_RW,
     &kq_calloutmax, 0, "Maximum number of callouts allocated for kqueue");
+static int             kq_checkloop = 1000000;
+SYSCTL_INT(_kern, OID_AUTO, kq_checkloop, CTLFLAG_RW,
+    &kq_checkloop, 0, "Maximum number of callouts allocated for kqueue");
 
 #define KNOTE_ACTIVATE(kn) do {                                        \
        kn->kn_status |= KN_ACTIVE;                                     \
@@ -418,25 +421,10 @@ void
 kqueue_terminate(struct kqueue *kq)
 {
        struct knote *kn;
-       struct klist *list;
-       int hv;
 
        while ((kn = TAILQ_FIRST(&kq->kq_knlist)) != NULL) {
                filter_detach(kn);
-               if (kn->kn_fop->f_flags & FILTEROP_ISFD) {
-                       list = &kn->kn_fp->f_klist;
-                       SLIST_REMOVE(list, kn, knote, kn_link);
-                       fdrop(kn->kn_fp);
-                       kn->kn_fp = NULL;
-               } else {
-                       hv = KN_HASH(kn->kn_id, kq->kq_knhashmask);
-                       list = &kq->kq_knhash[hv];
-                       SLIST_REMOVE(list, kn, knote, kn_link);
-               }
-               TAILQ_REMOVE(&kq->kq_knlist, kn, kn_kqlink);
-               if (kn->kn_status & KN_QUEUED)
-                       knote_dequeue(kn);
-               knote_free(kn);
+               knote_drop(kn);
        }
 
        if (kq->kq_knhash) {
@@ -531,6 +519,7 @@ kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
        struct timespec *tsp;
        int i, n, total, error, nerrors = 0;
        int lres;
+       int limit = kq_checkloop;
        struct kevent kev[KQ_NEVENTS];
        struct knote marker;
 
@@ -642,6 +631,8 @@ kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
                        if (error)
                                break;
                }
+               if (limit && --limit == 0)
+                       panic("kqueue: checkloop failed i=%d", i);
 
                /*
                 * Normally when fewer events are returned than requested
@@ -1224,12 +1215,13 @@ knote_drop(struct knote *kn)
        TAILQ_REMOVE(&kq->kq_knlist, kn, kn_kqlink);
        if (kn->kn_status & KN_QUEUED)
                knote_dequeue(kn);
-       if (kn->kn_fop->f_flags & FILTEROP_ISFD)
+       if (kn->kn_fop->f_flags & FILTEROP_ISFD) {
                fdrop(kn->kn_fp);
+               kn->kn_fp = NULL;
+       }
        knote_free(kn);
 }
 
-
 static void
 knote_enqueue(struct knote *kn)
 {