Do not call poll(2) or ppoll(2) with nfds > RLIMIT_NOFILE.
Both poll(2) and ppoll(2) will return EINVAL if the nfds function argument is larger than the max files per process resource limit. Prevent this by limiting the max number entries in the pfds[] array to the RLIMIT_NOFILE soft limit.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
@@ -81,29 +82,49 @@ sudo_ev_base_free_impl(struct sudo_event_base *base)
|
||||
int
|
||||
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
|
||||
{
|
||||
static int nofile_max = -1;
|
||||
struct pollfd *pfd;
|
||||
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT);
|
||||
|
||||
if (nofile_max == -1) {
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
|
||||
nofile_max = rlim.rlim_cur;
|
||||
}
|
||||
}
|
||||
|
||||
/* If out of space in pfds array, realloc. */
|
||||
if (base->pfd_free == base->pfd_max) {
|
||||
struct pollfd *pfds;
|
||||
int i;
|
||||
int i, new_max;
|
||||
|
||||
pfds =
|
||||
reallocarray(base->pfds, base->pfd_max, 2 * sizeof(struct pollfd));
|
||||
/* Don't allow pfd_max to go over RLIM_NOFILE */
|
||||
new_max = base->pfd_max * 2;
|
||||
if (new_max > nofile_max)
|
||||
new_max = nofile_max;
|
||||
if (base->pfd_free == new_max) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
||||
"%s: out of fds (max %d)", __func__, nofile_max);
|
||||
debug_return_int(-1);
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"%s: pfd_max %d -> %d", __func__, base->pfd_max, new_max);
|
||||
pfds = reallocarray(base->pfds, new_max, sizeof(struct pollfd));
|
||||
if (pfds == NULL) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
||||
"%s: unable to allocate %d pollfds", __func__, base->pfd_max * 2);
|
||||
"%s: unable to allocate %d pollfds", __func__, new_max);
|
||||
debug_return_int(-1);
|
||||
}
|
||||
base->pfds = pfds;
|
||||
base->pfd_max *= 2;
|
||||
base->pfd_max = new_max;
|
||||
for (i = base->pfd_free; i < base->pfd_max; i++) {
|
||||
base->pfds[i].fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in pfd entry. */
|
||||
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
|
||||
"%s: choosing free slot %d", __func__, base->pfd_free);
|
||||
ev->pfd_idx = base->pfd_free;
|
||||
pfd = &base->pfds[ev->pfd_idx];
|
||||
pfd->fd = ev->fd;
|
||||
@@ -133,8 +154,11 @@ sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
|
||||
|
||||
/* Mark pfd entry unused, add to free list and adjust high slot. */
|
||||
base->pfds[ev->pfd_idx].fd = -1;
|
||||
if (ev->pfd_idx < base->pfd_free)
|
||||
if (ev->pfd_idx < base->pfd_free) {
|
||||
base->pfd_free = ev->pfd_idx;
|
||||
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
|
||||
"%s: new free slot %d", __func__, base->pfd_free);
|
||||
}
|
||||
while (base->pfd_high >= 0 && base->pfds[base->pfd_high].fd == -1)
|
||||
base->pfd_high--;
|
||||
|
||||
@@ -182,16 +206,20 @@ sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
|
||||
}
|
||||
|
||||
nready = sudo_ev_poll(base->pfds, base->pfd_high + 1, timeout);
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
|
||||
switch (nready) {
|
||||
case -1:
|
||||
/* Error or interrupted by signal. */
|
||||
/* Error: EINTR (signal) or EINVAL (nfds > RLIMIT_NOFILE) */
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
||||
"sudo_ev_poll");
|
||||
debug_return_int(-1);
|
||||
case 0:
|
||||
/* Front end will activate timeout events. */
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: timeout", __func__);
|
||||
break;
|
||||
default:
|
||||
/* Activate each I/O event that fired. */
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__,
|
||||
nready);
|
||||
TAILQ_FOREACH(ev, &base->events, entries) {
|
||||
if (ev->pfd_idx != -1 && base->pfds[ev->pfd_idx].revents) {
|
||||
int what = 0;
|
||||
|
Reference in New Issue
Block a user