]> granicus.if.org Git - sudo/commitdiff
Make a copy of readfds/writefds before calling select() instead
authorTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 31 Oct 2013 22:13:15 +0000 (16:13 -0600)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 31 Oct 2013 22:13:15 +0000 (16:13 -0600)
of calculating it each time.  Keep track of high fd in the base.

common/alloc.c
common/event_select.c
include/sudo_event.h

index aa93319d188d60002c087ca8cb28bd20cf4dbfd5..b79a3a0d6edd2a98dce2062572ad643d2ecda5c3 100644 (file)
@@ -168,11 +168,10 @@ erealloc3(void *ptr, size_t nmemb, size_t size)
     return ptr;
 }
 
-#ifdef notyet
 /*
  * erecalloc() realloc(3)s nmemb * msize bytes and exits with an error
  * if overflow would occur or if the system malloc(3)/realloc(3) fails.
- * On success, the new space is zero-filled.  You can call ereallocz()
+ * On success, the new space is zero-filled.  You can call erealloc()
  * with a NULL pointer even if the system realloc(3) does not support this.
  */
 void *
@@ -195,7 +194,6 @@ erecalloc(void *ptr, size_t onmemb, size_t nmemb, size_t msize)
     }
     return ptr;
 }
-#endif
 
 /*
  * estrdup() is like strdup(3) except that it exits with an error if
index b5e79459c8b0369ea69705fef4050548de494abb..fc28d56e01f933e18937ddfbe4b8891ee57b2df4 100644 (file)
@@ -63,8 +63,10 @@ sudo_ev_base_alloc_impl(struct sudo_event_base *base)
     debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT)
 
     base->maxfd = NFDBITS - 1;
-    base->readfds = ecalloc(1, sizeof(fd_mask));
-    base->writefds = ecalloc(1, sizeof(fd_mask));
+    base->readfds_in = ecalloc(1, sizeof(fd_mask));
+    base->writefds_in = ecalloc(1, sizeof(fd_mask));
+    base->readfds_out = ecalloc(1, sizeof(fd_mask));
+    base->writefds_out = ecalloc(1, sizeof(fd_mask));
 
     debug_return_int(0);
 }
@@ -73,8 +75,10 @@ void
 sudo_ev_base_free_impl(struct sudo_event_base *base)
 {
     debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT)
-    efree(base->readfds);
-    efree(base->writefds);
+    efree(base->readfds_in);
+    efree(base->writefds_in);
+    efree(base->readfds_out);
+    efree(base->writefds_out);
     debug_return;
 }
 
@@ -85,14 +89,29 @@ sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
 
     /* If out of space in fd sets, realloc. */
     if (ev->fd > base->maxfd) {
+       const int o = (base->maxfd + 1) / NFDBITS;
        const int n = howmany(ev->fd + 1, NFDBITS);
-       efree(base->readfds);
-       efree(base->writefds);
-       base->readfds = ecalloc(n, sizeof(fd_mask));
-       base->writefds = ecalloc(n, sizeof(fd_mask));
+       base->readfds_in = erecalloc(base->readfds_in, o, n, sizeof(fd_mask));
+       base->writefds_in = erecalloc(base->writefds_in, o, n, sizeof(fd_mask));
+       base->readfds_out = erecalloc(base->readfds_out, o, n, sizeof(fd_mask));
+       base->writefds_out = erecalloc(base->writefds_out, o, n, sizeof(fd_mask));
        base->maxfd = (n * NFDBITS) - 1;
     }
 
+    /* Set events and adjust high fd as needed. */
+    if (ISSET(ev->events, SUDO_EV_READ)) {
+       sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to readfs",
+           __func__, ev->fd);
+       FD_SET(ev->fd, base->readfds_in);
+    }
+    if (ISSET(ev->events, SUDO_EV_WRITE)) {
+       sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to writefds",
+           __func__, ev->fd);
+       FD_SET(ev->fd, base->writefds_in);
+    }
+    if (ev->fd > base->highfd)
+       base->highfd = ev->fd;
+
     debug_return_int(0);
 }
 
@@ -101,9 +120,18 @@ sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
 {
     debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT)
 
-    /* Remove from readfds and writefds and adjust event count. */
-    FD_CLR(ev->fd, base->readfds);
-    FD_CLR(ev->fd, base->writefds);
+    /* Remove from readfds and writefds and adjust high fd. */
+    FD_CLR(ev->fd, base->readfds_in);
+    FD_CLR(ev->fd, base->writefds_in);
+    if (base->highfd == ev->fd) {
+       for (;;) {
+           if (--base->highfd < 0)
+               break;
+           if (FD_ISSET(base->highfd, base->readfds_in) ||
+               FD_ISSET(base->highfd, base->writefds_in))
+               break;
+       }
+    }
 
     debug_return_int(0);
 }
@@ -113,7 +141,8 @@ sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
 {
     struct timeval now, tv, *timeout;
     struct sudo_event *ev;
-    int nready, highfd = 0;
+    size_t setsize;
+    int nready;
     debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
 
     if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
@@ -132,28 +161,15 @@ sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
        }
     }
 
-    /* For select we need to redo readfds and writefds each time. */
-    memset(base->readfds, 0, howmany(base->maxfd + 1, NFDBITS) * sizeof(fd_mask));
-    memset(base->writefds, 0, howmany(base->maxfd + 1, NFDBITS) * sizeof(fd_mask));
-    TAILQ_FOREACH(ev, &base->events, entries) {
-       if (ISSET(ev->events, SUDO_EV_READ)) {
-           sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to readfs",
-               __func__, ev->fd);
-           FD_SET(ev->fd, base->readfds);
-           if (ev->fd > highfd)
-               highfd = ev->fd;
-       }
-       if (ISSET(ev->events, SUDO_EV_WRITE)) {
-           sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to writefds",
-               __func__, ev->fd);
-           FD_SET(ev->fd, base->writefds);
-           if (ev->fd > highfd)
-               highfd = ev->fd;
-       }
-    }
+    /* select() overwrites readfds/writefds so make a copy. */
+    setsize = howmany(base->highfd + 1, NFDBITS) * sizeof(fd_mask);
+    memcpy(base->readfds_out, base->readfds_in, setsize);
+    memcpy(base->writefds_out, base->writefds_in, setsize);
+
     sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: select high fd %d",
-       __func__, highfd);
-    nready = select(highfd + 1, base->readfds, base->writefds, NULL, timeout);
+       __func__, base->highfd);
+    nready = select(base->highfd + 1, base->readfds_out, base->writefds_out,
+       NULL, timeout);
     sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
     switch (nready) {
     case -1:
@@ -166,9 +182,9 @@ sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
        /* Activate each I/O event that fired. */
        TAILQ_FOREACH(ev, &base->events, entries) {
            int what = 0;
-           if (FD_ISSET(ev->fd, base->readfds))
+           if (FD_ISSET(ev->fd, base->readfds_out))
                what |= (ev->events & SUDO_EV_READ);
-           if (FD_ISSET(ev->fd, base->writefds))
+           if (FD_ISSET(ev->fd, base->writefds_out))
                what |= (ev->events & SUDO_EV_WRITE);
            if (what != 0) {
                /* Make event active. */
index 09ff724f2c9f6e10b77132e1cc90a65821ce0a63..1a979755e3459dd1ffaf93b5f2d55bac6e2b9fab 100644 (file)
@@ -72,9 +72,12 @@ struct sudo_event_base {
     int pfd_high;              /* highest slot used */
     int pfd_free;              /* idx of next free entry or pfd_max if full */
 #else
-    fd_set *readfds;           /* read I/O descriptor set */
-    fd_set *writefds;          /* write I/O descriptor set */
+    fd_set *readfds_in;                /* read I/O descriptor set (in) */
+    fd_set *writefds_in;       /* write I/O descriptor set (in) */
+    fd_set *readfds_out;       /* read I/O descriptor set (out) */
+    fd_set *writefds_out;      /* write I/O descriptor set (out) */
     int maxfd;                 /* max fd we can store in readfds/writefds */
+    int highfd;                        /* highest fd to pass as 1st arg to select */
 #endif /* HAVE_POLL */
     unsigned int flags;                /* SUDO_EVBASE_* */
 };