tevent: make sure we cleanup the array passed to poll() after deleting an event
authorStefan Metzmacher <metze@samba.org>
Thu, 21 Feb 2013 13:41:24 +0000 (14:41 +0100)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 Mar 2013 09:48:39 +0000 (10:48 +0100)
If we don't cleanup the array passed to poll after an
event was deleted, we may pass a bad file descriptor to poll().

This was found by the following test failure in Samba's
autobuild, while removing the epoll support from
the "standard" backend.

    [48/1555 in 4m37s] samba3.smbtorture_s3.plain(s3dc).LOCK4
    UNEXPECTED(failure): samba3.smbtorture_s3.plain(s3dc).LOCK4.smbtorture(s3dc)
    REASON: _StringException: _StringException: using seed 1361530718
    host=127.0.0.2 share=tmp user=metze myname=sn-devel-104
    Running LOCK4
    starting locktest4
    Failed to create file: NT_STATUS_INVALID_HANDLE
    finished locktest4
    TEST LOCK4 FAILED!
    LOCK4 took 190.492 secs

Signed-off-by: Stefan Metzmacher <metze@samba.org>
lib/tevent/tevent_poll.c

index 68885e94c0feb09f8c37e00dc096155ea95e0656..0928cbd6cb07f46cebb76ac995da9897c93f91ad 100644 (file)
@@ -38,6 +38,7 @@ struct poll_event_context {
         * picked up yet by poll_event_loop_once
         */
        struct tevent_fd *fresh;
+       bool deleted;
 
        /*
         * These two arrays are maintained together.
@@ -219,6 +220,7 @@ static int poll_event_fd_destructor(struct tevent_fd *fde)
                ev->additional_data, struct poll_event_context);
 
        poll_ev->fdes[del_idx] = NULL;
+       poll_ev->deleted = true;
        poll_event_wake_pollthread(poll_ev);
 done:
        return tevent_common_fd_destructor(fde);
@@ -363,6 +365,34 @@ static bool poll_event_setup_fresh(struct tevent_context *ev,
        struct tevent_fd *fde, *next;
        unsigned num_fresh, num_fds;
 
+       if (poll_ev->deleted) {
+               unsigned first_fd = (poll_ev->signal_fd != -1) ? 1 : 0;
+               unsigned i;
+
+               for (i=first_fd; i < poll_ev->num_fds;) {
+                       fde = poll_ev->fdes[i];
+                       if (fde != NULL) {
+                               i++;
+                               continue;
+                       }
+
+                       /*
+                        * This fde was talloc_free()'ed. Delete it
+                        * from the arrays
+                        */
+                       poll_ev->num_fds -= 1;
+                       if (poll_ev->num_fds == i) {
+                               break;
+                       }
+                       poll_ev->fds[i] = poll_ev->fds[poll_ev->num_fds];
+                       poll_ev->fdes[i] = poll_ev->fdes[poll_ev->num_fds];
+                       if (poll_ev->fdes[i] != NULL) {
+                               poll_ev->fdes[i]->additional_flags = i;
+                       }
+               }
+       }
+       poll_ev->deleted = false;
+
        if (poll_ev->fresh == NULL) {
                return true;
        }