If event loop fails due to ENXIO, remove /dev/tty events and recover.

This fixes an issue on Solaris 11.4 (and probably others) with "sudo
reboot" when I/O logging is enabled.  Previously, sudo would kill
the command if it was still running after the event loop terminated,
leaving the system in a half-dead state.
This commit is contained in:
Todd C. Miller
2020-06-02 08:59:38 -06:00
parent 592eb7ab49
commit a380709215

View File

@@ -1,7 +1,7 @@
/* /*
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* Copyright (c) 2009-2019 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2009-2020 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -1347,6 +1347,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
bool interpose[3] = { false, false, false }; bool interpose[3] = { false, false, false };
struct exec_closure_pty ec = { 0 }; struct exec_closure_pty ec = { 0 };
struct plugin_container *plugin; struct plugin_container *plugin;
int evloop_retries = -1;
sigset_t set, oset; sigset_t set, oset;
struct sigaction sa; struct sigaction sa;
struct stat sb; struct stat sb;
@@ -1623,8 +1624,10 @@ exec_pty(struct command_details *details, struct command_status *cstat)
/* /*
* In the event loop we pass input from user tty to master * In the event loop we pass input from user tty to master
* and pass output from master to stdout and IO plugin. * and pass output from master to stdout and IO plugin.
* Try to recover on ENXIO, it means the tty was revoked.
*/ */
add_io_events(ec.evbase); add_io_events(ec.evbase);
do {
if (sudo_ev_dispatch(ec.evbase) == -1) if (sudo_ev_dispatch(ec.evbase) == -1)
sudo_warn(U_("error in event loop")); sudo_warn(U_("error in event loop"));
if (sudo_ev_got_break(ec.evbase)) { if (sudo_ev_got_break(ec.evbase)) {
@@ -1639,7 +1642,20 @@ exec_pty(struct command_details *details, struct command_status *cstat)
cstat->type = CMD_WSTATUS; cstat->type = CMD_WSTATUS;
cstat->val = W_EXITCODE(1, SIGKILL); cstat->val = W_EXITCODE(1, SIGKILL);
} }
} else if (!sudo_ev_got_exit(ec.evbase)) {
switch (errno) {
case ENXIO:
case EIO:
case EBADF:
/* /dev/tty was revoked, remove tty events and retry (once) */
if (evloop_retries == -1 && io_fds[SFD_USERTTY] != -1) {
ev_free_by_fd(ec.evbase, io_fds[SFD_USERTTY]);
evloop_retries = 1;
} }
break;
}
}
} while (evloop_retries-- > 0);
/* Flush any remaining output, free I/O bufs and events, do logout. */ /* Flush any remaining output, free I/O bufs and events, do logout. */
pty_finish(cstat); pty_finish(cstat);