Don't use SUDO_EVLOOP_NONBLOCK when flushing buffers at pty close

time, only when the user suspends sudo.  Fixes a problem where all
buffers might not get flushed at exit when logging I/O.  Reproducible
via "sudo tar cf - foo | (cd /tmp && sudo tar xf -)" on OpenBSD.
This commit is contained in:
Todd C. Miller
2016-05-17 08:16:43 -06:00
parent eb4510597b
commit 3b6cb9d65b

View File

@@ -90,7 +90,7 @@ static pid_t ppgrp, cmnd_pgrp, mon_pgrp;
static sigset_t ttyblock; static sigset_t ttyblock;
static struct io_buffer_list iobufs; static struct io_buffer_list iobufs;
static void del_io_events(void); static void del_io_events(int flags);
static int exec_monitor(struct command_details *details, int backchannel); static int exec_monitor(struct command_details *details, int backchannel);
static void exec_pty(struct command_details *details, static void exec_pty(struct command_details *details,
struct command_status *cstat, int errfd); struct command_status *cstat, int errfd);
@@ -447,7 +447,7 @@ suspend_parent(int signo)
case SIGSTOP: case SIGSTOP:
case SIGTSTP: case SIGTSTP:
/* Flush any remaining output and deschedule I/O events. */ /* Flush any remaining output and deschedule I/O events. */
del_io_events(); del_io_events(SUDO_EVLOOP_NONBLOCK);
/* Restore original tty mode before suspending. */ /* Restore original tty mode before suspending. */
if (ttymode != TERM_COOKED) if (ttymode != TERM_COOKED)
@@ -885,7 +885,7 @@ pty_close(struct command_status *cstat)
int n; int n;
debug_decl(pty_close, SUDO_DEBUG_EXEC); debug_decl(pty_close, SUDO_DEBUG_EXEC);
/* Flush any remaining output (the plugin already got it) */ /* Flush any remaining output (the plugin already got it). */
if (io_fds[SFD_USERTTY] != -1) { if (io_fds[SFD_USERTTY] != -1) {
n = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0); n = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0);
if (n != -1 && ISSET(n, O_NONBLOCK)) { if (n != -1 && ISSET(n, O_NONBLOCK)) {
@@ -893,7 +893,7 @@ pty_close(struct command_status *cstat)
(void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n); (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n);
} }
} }
del_io_events(); del_io_events(0);
/* Free I/O buffers. */ /* Free I/O buffers. */
while ((iob = SLIST_FIRST(&iobufs)) != NULL) { while ((iob = SLIST_FIRST(&iobufs)) != NULL) {
@@ -974,7 +974,7 @@ add_io_events(struct sudo_event_base *evbase)
* than /dev/tty. Removes I/O events from the event base when done. * than /dev/tty. Removes I/O events from the event base when done.
*/ */
static void static void
del_io_events(void) del_io_events(int flags)
{ {
struct io_buffer *iob; struct io_buffer *iob;
struct sudo_event_base *evbase; struct sudo_event_base *evbase;
@@ -1019,17 +1019,20 @@ del_io_events(void)
} }
} }
(void) sudo_ev_loop(evbase, SUDO_EVLOOP_NONBLOCK); (void) sudo_ev_loop(evbase, flags);
if (!ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
/* In blocking mode we should have flushed all buffers. */
SLIST_FOREACH(iob, &iobufs, entries) { SLIST_FOREACH(iob, &iobufs, entries) {
if (iob->wevent != NULL) { if (iob->wevent != NULL) {
if (iob->len > iob->off) { if (iob->len > iob->off) {
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_ERROR,
"unflushed data: wevent %p, fd %d, events %d", "unflushed data: wevent %p, fd %d, events %d",
iob->wevent, iob->wevent->fd, iob->wevent->events); iob->wevent, iob->wevent->fd, iob->wevent->events);
} }
} }
} }
}
/* Free temporary event base, removing its events. */ /* Free temporary event base, removing its events. */
sudo_ev_base_free(evbase); sudo_ev_base_free(evbase);