Redo preserve_fds support to remap high fds so we can get the most

out of closefrom().  The fds are then restored after closefrom().
This commit is contained in:
Todd C. Miller
2013-12-24 15:01:00 -07:00
parent 76544011ed
commit 4d80e7cea4
3 changed files with 132 additions and 39 deletions

View File

@@ -16,13 +16,7 @@
#include <config.h>
#include <sys/param.h> /* for howmany() on Linux */
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h> /* for howmany() on Solaris */
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h> /* for FD_* macros */
#endif /* HAVE_SYS_SELECT_H */
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
@@ -53,21 +47,62 @@
/*
* Add an fd to preserve.
*/
void
add_preserved_fd(struct preserved_fds *pfds, int fd)
int
add_preserved_fd(struct preserved_fd_list *pfds, int fd)
{
struct preserved_fd *pfd, *pfd_new;
debug_decl(add_preserved_fd, SUDO_DEBUG_UTIL)
/* Reallocate as needed before adding. */
if (fd > pfds->maxfd) {
int osize = howmany(pfds->maxfd + 1, NFDBITS);
int nsize = howmany(fd + 1, NFDBITS);
if (nsize > osize)
pfds->fds = erecalloc(pfds->fds, osize, nsize, sizeof(fd_mask));
pfds->maxfd = fd;
pfd_new = emalloc(sizeof(*pfd));
pfd_new->lowfd = fd;
pfd_new->highfd = fd;
pfd_new->flags = fcntl(fd, F_GETFD);
if (pfd_new->flags == -1) {
efree(pfd_new);
debug_return_int(-1);
}
FD_SET(fd, pfds->fds);
TAILQ_FOREACH(pfd, pfds, entries) {
if (fd == pfd->highfd) {
/* already preserved */
efree(pfd_new);
break;
}
if (fd < pfd->highfd) {
TAILQ_INSERT_BEFORE(pfd, pfd_new, entries);
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"preserving fd %d", fd);
break;
}
}
if (pfd == NULL) {
TAILQ_INSERT_TAIL(pfds, pfd_new, entries);
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"preserving fd %d", fd);
}
debug_return_int(0);
}
/*
* Close fds in the range [from,to]
*/
static void
closefrom_range(int from, int to)
{
debug_decl(closefrom_range, SUDO_DEBUG_UTIL)
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"closing fds [%d, %d]", from, to);
while (from <= to) {
#ifdef __APPLE__
/* Avoid potential libdispatch crash when we close its fds. */
(void) fcntl(from, F_SETFD, FD_CLOEXEC);
#else
(void) close(from);
#endif
from++;
}
debug_return;
}
@@ -76,27 +111,82 @@ add_preserved_fd(struct preserved_fds *pfds, int fd)
* in pfds.
*/
void
closefrom_except(int startfd, struct preserved_fds *pfds)
closefrom_except(int startfd, struct preserved_fd_list *pfds)
{
int fd;
int tmpfd;
struct preserved_fd *pfd, *pfd_next;
debug_decl(closefrom_except, SUDO_DEBUG_UTIL)
/* First handle the preserved fds. */
if (startfd <= pfds->maxfd) {
for (fd = startfd; fd <= pfds->maxfd; fd++) {
if (!FD_ISSET(fd, pfds->fds)) {
#ifdef __APPLE__
/* Avoid potential libdispatch crash when we close its fds. */
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
#else
(void) close(fd);
#endif
/*
* First, relocate preserved fds to be as contiguous as possible.
*/
TAILQ_FOREACH_SAFE(pfd, pfds, entries, pfd_next) {
if (pfd->highfd < startfd)
continue;
tmpfd = dup(pfd->highfd);
if (tmpfd < pfd->highfd) {
if (tmpfd == -1) {
if (errno == EBADF)
TAILQ_REMOVE(pfds, pfd, entries);
continue;
}
pfd->lowfd = tmpfd;
tmpfd = pfd->highfd;
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"dup %d -> %d", pfd->highfd, pfd->lowfd);
}
startfd = pfds->maxfd + 1;
(void) close(tmpfd);
}
if (TAILQ_EMPTY(pfds)) {
/* No fds to preserve. */
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"closefrom(%d)", startfd);
closefrom(startfd);
debug_return;
}
/* Close any fds [startfd,TAILQ_FIRST(pfds)->lowfd) */
closefrom_range(startfd, TAILQ_FIRST(pfds)->lowfd - 1);
/* Close any unpreserved fds (TAILQ_LAST(pfds)->lowfd,startfd) */
TAILQ_FOREACH_SAFE(pfd, pfds, entries, pfd_next) {
if (pfd->lowfd < startfd)
continue;
if (pfd_next != NULL && pfd->lowfd + 1 != pfd_next->lowfd)
closefrom_range(pfd->lowfd + 1, pfd_next->lowfd);
}
/* Let closefrom() do the rest for us. */
pfd = TAILQ_LAST(pfds, preserved_fd_list);
if (pfd != NULL && pfd->lowfd + 1 > startfd)
startfd = pfd->lowfd + 1;
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"closefrom(%d)", startfd);
closefrom(startfd);
/* Restore preserved fds and set flags. */
TAILQ_FOREACH(pfd, pfds, entries) {
if (pfd->lowfd != pfd->highfd) {
if (dup2(pfd->lowfd, pfd->highfd) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"dup2(%d, %d): %s", pfd->lowfd, pfd->highfd,
strerror(errno));
} else {
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"dup2(%d, %d)", pfd->lowfd, pfd->highfd);
}
if (fcntl(pfd->highfd, F_SETFL, pfd->flags) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"fcntl(%d, F_SETFL, %d): %s", pfd->highfd,
pfd->flags, strerror(errno));
} else {
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"fcntl(%d, F_SETFL, %d)", pfd->highfd, pfd->flags);
}
(void) close(pfd->lowfd);
}
}
debug_return;
}
@@ -104,7 +194,7 @@ closefrom_except(int startfd, struct preserved_fds *pfds)
* Parse a comma-separated list of fds and add them to preserved_fds.
*/
void
parse_preserved_fds(struct preserved_fds *pfds, const char *fdstr)
parse_preserved_fds(struct preserved_fd_list *pfds, const char *fdstr)
{
const char *cp = fdstr;
long lval;

View File

@@ -536,7 +536,7 @@ command_info_to_details(char * const info[], struct command_details *details)
memset(details, 0, sizeof(*details));
details->closefrom = -1;
details->preserved_fds.maxfd = -1;
TAILQ_INIT(&details->preserved_fds);
#define SET_STRING(s, n) \
if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \

View File

@@ -122,10 +122,13 @@ struct user_details {
#define CD_SET_UTMP 0x2000
#define CD_EXEC_BG 0x4000
struct preserved_fds {
int maxfd;
fd_set *fds;
struct preserved_fd {
TAILQ_ENTRY(preserved_fd) entries;
int lowfd;
int highfd;
int flags;
};
TAILQ_HEAD(preserved_fd_list, preserved_fd);
struct command_details {
uid_t uid;
@@ -138,7 +141,7 @@ struct command_details {
int ngroups;
int closefrom;
int flags;
struct preserved_fds preserved_fds;
struct preserved_fd_list preserved_fds;
struct passwd *pw;
GETGROUPS_T *groups;
const char *command;
@@ -247,8 +250,8 @@ void save_signals(void);
void preload_static_symbols(void);
/* preserve_fds.c */
void add_preserved_fd(struct preserved_fds *pfds, int fd);
void closefrom_except(int startfd, struct preserved_fds *pfds);
void parse_preserved_fds(struct preserved_fds *pfds, const char *fdstr);
int add_preserved_fd(struct preserved_fd_list *pfds, int fd);
void closefrom_except(int startfd, struct preserved_fd_list *pfds);
void parse_preserved_fds(struct preserved_fd_list *pfds, const char *fdstr);
#endif /* _SUDO_SUDO_H */