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 <config.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#ifdef HAVE_STDBOOL_H
|
#ifdef HAVE_STDBOOL_H
|
||||||
@@ -81,29 +82,49 @@ sudo_ev_base_free_impl(struct sudo_event_base *base)
|
|||||||
int
|
int
|
||||||
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
|
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
|
||||||
{
|
{
|
||||||
|
static int nofile_max = -1;
|
||||||
struct pollfd *pfd;
|
struct pollfd *pfd;
|
||||||
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT);
|
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 out of space in pfds array, realloc. */
|
||||||
if (base->pfd_free == base->pfd_max) {
|
if (base->pfd_free == base->pfd_max) {
|
||||||
struct pollfd *pfds;
|
struct pollfd *pfds;
|
||||||
int i;
|
int i, new_max;
|
||||||
|
|
||||||
pfds =
|
/* Don't allow pfd_max to go over RLIM_NOFILE */
|
||||||
reallocarray(base->pfds, base->pfd_max, 2 * sizeof(struct pollfd));
|
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) {
|
if (pfds == NULL) {
|
||||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
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);
|
debug_return_int(-1);
|
||||||
}
|
}
|
||||||
base->pfds = pfds;
|
base->pfds = pfds;
|
||||||
base->pfd_max *= 2;
|
base->pfd_max = new_max;
|
||||||
for (i = base->pfd_free; i < base->pfd_max; i++) {
|
for (i = base->pfd_free; i < base->pfd_max; i++) {
|
||||||
base->pfds[i].fd = -1;
|
base->pfds[i].fd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in pfd entry. */
|
/* 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;
|
ev->pfd_idx = base->pfd_free;
|
||||||
pfd = &base->pfds[ev->pfd_idx];
|
pfd = &base->pfds[ev->pfd_idx];
|
||||||
pfd->fd = ev->fd;
|
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. */
|
/* Mark pfd entry unused, add to free list and adjust high slot. */
|
||||||
base->pfds[ev->pfd_idx].fd = -1;
|
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;
|
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)
|
while (base->pfd_high >= 0 && base->pfds[base->pfd_high].fd == -1)
|
||||||
base->pfd_high--;
|
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);
|
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) {
|
switch (nready) {
|
||||||
case -1:
|
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);
|
debug_return_int(-1);
|
||||||
case 0:
|
case 0:
|
||||||
/* Front end will activate timeout events. */
|
/* Front end will activate timeout events. */
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: timeout", __func__);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Activate each I/O event that fired. */
|
/* 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) {
|
TAILQ_FOREACH(ev, &base->events, entries) {
|
||||||
if (ev->pfd_idx != -1 && base->pfds[ev->pfd_idx].revents) {
|
if (ev->pfd_idx != -1 && base->pfds[ev->pfd_idx].revents) {
|
||||||
int what = 0;
|
int what = 0;
|
||||||
|
Reference in New Issue
Block a user