]> git.hungrycats.org Git - linux/commitdiff
[PATCH] epoll cross-thread deletion fix
authorAndrew Morton <akpm@digeo.com>
Wed, 9 Apr 2003 04:31:19 +0000 (21:31 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Wed, 9 Apr 2003 04:31:19 +0000 (21:31 -0700)
From: Davide Libenzi <davidel@xmailserver.org>

This fixes a bug that might happen having a thread doing epoll_wait() with
another thread doing epoll_ctl(EPOLL_CTL_DEL) and close(). The fast check
inside eventpoll_release() is good to not effect performace of code not
using epoll, but it requires get_file() to be called ( that can be avoided
by dropping the fast check ). I opted to keep the fast check and to have
epoll to call get_file() before the event send loop. I tested it on UP and
2SMP with a bug-exploiting program provided by @pivia.com ( thx to them )
and it looks fine. I also update the 2.4.20 epoll patch with this fix :

fs/eventpoll.c

index 0a3d2476866434c0be1a0df29b50591bc2971855..da1114d24455a41b61a8a1028326d6d4ae42a672 100644 (file)
@@ -1341,6 +1341,13 @@ static int ep_collect_ready_items(struct eventpoll *ep, struct list_head *txlist
                         */
                        ep_use_epitem(epi);
 
+                       /*
+                        * We need to increase the usage count of the "struct file" because
+                        * another thread might call close() on this target and make the file
+                        * to vanish before we will be able to call f_op->poll().
+                        */
+                       get_file(epi->file);
+
                        /*
                         * This is initialized in this way so that the default
                         * behaviour of the reinjecting code will be to push back
@@ -1385,6 +1392,14 @@ static int ep_send_events(struct eventpoll *ep, struct list_head *txlist,
                /* Get the ready file event set */
                revents = epi->file->f_op->poll(epi->file, NULL);
 
+               /*
+                * Release the file usage before checking the event mask.
+                * In case this call will lead to the file removal, its
+                * ->event.events member has been already set to zero and
+                * this will make the event to be dropped.
+                */
+               fput(epi->file);
+
                /*
                 * Set the return event set for the current file descriptor.
                 * Note that only the task task was successfully able to link
@@ -1398,8 +1413,17 @@ static int ep_send_events(struct eventpoll *ep, struct list_head *txlist,
                        eventbuf++;
                        if (eventbuf == EP_MAX_BUF_EVENTS) {
                                if (__copy_to_user(&events[eventcnt], event,
-                                                  eventbuf * sizeof(struct epoll_event)))
+                                                  eventbuf * sizeof(struct epoll_event))) {
+                                       /*
+                                        * We need to complete the loop to decrement the file
+                                        * usage before returning from this function.
+                                        */
+                                       for (lnk = lnk->next; lnk != txlist; lnk = lnk->next) {
+                                               epi = list_entry(lnk, struct epitem, txlink);
+                                               fput(epi->file);
+                                       }
                                        return -EFAULT;
+                               }
                                eventcnt += eventbuf;
                                eventbuf = 0;
                        }