Handle receipt of SIGTTIN/SIGTTOU when reading/writing from/to the tty.

We can't use a signal event for these since that would restart the
system call after the signal was handled and the callback would not
get a chance to run.  Fixes running a command in the background that
write to the tty when the TOSTOP terminal flag is set.
This commit is contained in:
Todd C. Miller
2017-11-29 12:06:12 -07:00
parent 5ccc7ab879
commit 54acf4f991

View File

@@ -110,6 +110,7 @@ static int safe_close(int fd);
static void ev_free_by_fd(struct sudo_event_base *evbase, int fd); static void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
static void check_foreground(pid_t ppgrp); static void check_foreground(pid_t ppgrp);
static void add_io_events(struct sudo_event_base *evbase); static void add_io_events(struct sudo_event_base *evbase);
static void schedule_signal(struct exec_closure_pty *ec, int signo);
/* /*
* Cleanup hook for sudo_fatal()/sudo_fatalx() * Cleanup hook for sudo_fatal()/sudo_fatalx()
@@ -517,6 +518,17 @@ suspend_sudo(int signo, pid_t ppgrp)
debug_return_int(ret); debug_return_int(ret);
} }
/*
* SIGTTIN signal handler for read_callback that just sets a flag.
*/
static volatile sig_atomic_t got_sigttin;
static void
sigttin(int signo)
{
got_sigttin = 1;
}
/* /*
* Read an iobuf that is ready. * Read an iobuf that is ready.
*/ */
@@ -524,17 +536,36 @@ static void
read_callback(int fd, int what, void *v) read_callback(int fd, int what, void *v)
{ {
struct io_buffer *iob = v; struct io_buffer *iob = v;
struct sudo_event_base *evbase; struct sudo_event_base *evbase = sudo_ev_get_base(iob->revent);
int n; struct sigaction sa, osa;
int saved_errno;
ssize_t n;
debug_decl(read_callback, SUDO_DEBUG_EXEC); debug_decl(read_callback, SUDO_DEBUG_EXEC);
evbase = sudo_ev_get_base(iob->revent); /*
do { * We ignore SIGTTIN by default but we need to handle it when reading
n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len); * from the terminal. A signal event won't work here because the
} while (n == -1 && errno == EINTR); * read() would be restarted, preventing the callback from running.
*/
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = sigttin;
got_sigttin = 0;
sigaction(SIGTTIN, &sa, &osa);
n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len);
saved_errno = errno;
sigaction(SIGTTIN, &osa, NULL);
errno = saved_errno;
switch (n) { switch (n) {
case -1: case -1:
if (errno == EAGAIN) if (got_sigttin) {
/* Schedule SIGTTIN to be forwared to the command. */
schedule_signal(iob->ec, SIGTTIN);
/* Restart event loop to service signal immediately. */
sudo_ev_loopcontinue(evbase);
}
if (errno == EAGAIN || errno == EINTR)
break; break;
/* treat read error as fatal and close the fd */ /* treat read error as fatal and close the fd */
sudo_debug_printf(SUDO_DEBUG_ERROR, sudo_debug_printf(SUDO_DEBUG_ERROR,
@@ -557,7 +588,7 @@ read_callback(int fd, int what, void *v)
break; break;
default: default:
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_INFO,
"read %d bytes from fd %d", n, fd); "read %zd bytes from fd %d", n, fd);
if (!iob->action(iob->buf + iob->len, n, iob)) { if (!iob->action(iob->buf + iob->len, n, iob)) {
terminate_command(iob->ec->cmnd_pid, true); terminate_command(iob->ec->cmnd_pid, true);
iob->ec->cmnd_pid = -1; iob->ec->cmnd_pid = -1;
@@ -577,6 +608,17 @@ read_callback(int fd, int what, void *v)
} }
} }
/*
* SIGTTOU signal handler for write_callback that just sets a flag.
*/
static volatile sig_atomic_t got_sigttou;
static void
sigttou(int signo)
{
got_sigttou = 1;
}
/* /*
* Write an iobuf that is ready. * Write an iobuf that is ready.
*/ */
@@ -584,14 +626,27 @@ static void
write_callback(int fd, int what, void *v) write_callback(int fd, int what, void *v)
{ {
struct io_buffer *iob = v; struct io_buffer *iob = v;
struct sudo_event_base *evbase; struct sudo_event_base *evbase = sudo_ev_get_base(iob->wevent);
int n; struct sigaction sa, osa;
int saved_errno;
ssize_t n;
debug_decl(write_callback, SUDO_DEBUG_EXEC); debug_decl(write_callback, SUDO_DEBUG_EXEC);
evbase = sudo_ev_get_base(iob->wevent); /*
do { * We ignore SIGTTOU by default but we need to handle it when writing
n = write(fd, iob->buf + iob->off, iob->len - iob->off); * to the terminal. A signal event won't work here because the
} while (n == -1 && errno == EINTR); * write() would be restarted, preventing the callback from running.
*/
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = sigttou;
got_sigttou = 0;
sigaction(SIGTTOU, &sa, &osa);
n = write(fd, iob->buf + iob->off, iob->len - iob->off);
saved_errno = errno;
sigaction(SIGTTOU, &osa, NULL);
errno = saved_errno;
if (n == -1) { if (n == -1) {
switch (errno) { switch (errno) {
case EPIPE: case EPIPE:
@@ -610,6 +665,14 @@ write_callback(int fd, int what, void *v)
safe_close(fd); safe_close(fd);
ev_free_by_fd(evbase, fd); ev_free_by_fd(evbase, fd);
break; break;
case EINTR:
if (got_sigttou) {
/* Schedule SIGTTOU to be forwared to the command. */
schedule_signal(iob->ec, SIGTTOU);
/* Restart event loop to service signal immediately. */
sudo_ev_loopcontinue(evbase);
}
/* FALLTHROUGH */
case EAGAIN: case EAGAIN:
/* not an error */ /* not an error */
break; break;
@@ -624,7 +687,7 @@ write_callback(int fd, int what, void *v)
} }
} else { } else {
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_INFO,
"wrote %d bytes to fd %d", n, fd); "wrote %zd bytes to fd %d", n, fd);
iob->off += n; iob->off += n;
/* Reset buffer if fully consumed. */ /* Reset buffer if fully consumed. */
if (iob->off == iob->len) { if (iob->off == iob->len) {