terminal-unix: move all processing out of sighandler

commit fa9e1f06f tried to move signal unsafe operations out of
signal handlers but mistakenly introduced a race. before,
sigtstop would process the following in order:

0. do_deactivate_getch2();
1. raise(SIGTSTP)

that commit moved 0 out of the signal handler (due to it being
unsafe) but kept 1 in there. this may mess up the ordering of
these operations. this commit moves everything out of the
handler so that things happen in proper order.

since things are now moved out of the handler, SA_RESETHAND is
no longer being applied to SIGTSTP. since that can result in
races if multiple signals are delivered faster than we can
respond to them.
This commit is contained in:
NRK 2024-01-06 15:15:48 +00:00 committed by sfan5
parent dfecc9f083
commit b75b56f910
1 changed files with 16 additions and 23 deletions

View File

@ -324,7 +324,7 @@ static int setsigaction(int signo, void (*handler) (int),
struct sigaction sa; struct sigaction sa;
sa.sa_handler = handler; sa.sa_handler = handler;
if(do_mask) if (do_mask)
sigfillset(&sa.sa_mask); sigfillset(&sa.sa_mask);
else else
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
@ -354,26 +354,11 @@ static int death_pipe[2] = {-1, -1};
enum { PIPE_STOP, PIPE_CONT }; enum { PIPE_STOP, PIPE_CONT };
static int stop_cont_pipe[2] = {-1, -1}; static int stop_cont_pipe[2] = {-1, -1};
static void stop_sighandler(int signum) static void stop_cont_sighandler(int signum)
{ {
int saved_errno = errno; int saved_errno = errno;
(void)write(stop_cont_pipe[1], &(char){PIPE_STOP}, 1); char sig = signum == SIGCONT ? PIPE_CONT : PIPE_STOP;
(void)write(STDERR_FILENO, TERM_ESC_RESTORE_CURSOR, (void)write(stop_cont_pipe[1], &sig, 1);
sizeof(TERM_ESC_RESTORE_CURSOR) - 1);
errno = saved_errno;
// note: for this signal, we use SA_RESETHAND but do NOT mask signals
// so this will invoke the default handler
raise(SIGTSTP);
}
static void continue_sighandler(int signum)
{
int saved_errno = errno;
// SA_RESETHAND has reset SIGTSTP, so we need to restore it here
setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false);
(void)write(stop_cont_pipe[1], &(char){PIPE_CONT}, 1);
errno = saved_errno; errno = saved_errno;
} }
@ -437,11 +422,19 @@ static MP_THREAD_VOID terminal_thread(void *ptr)
if (fds[1].revents & POLLIN) { if (fds[1].revents & POLLIN) {
int8_t c = -1; int8_t c = -1;
(void)read(stop_cont_pipe[0], &c, 1); (void)read(stop_cont_pipe[0], &c, 1);
if (c == PIPE_STOP) if (c == PIPE_STOP) {
do_deactivate_getch2(); do_deactivate_getch2();
else if (c == PIPE_CONT) (void)write(STDERR_FILENO, TERM_ESC_RESTORE_CURSOR,
sizeof(TERM_ESC_RESTORE_CURSOR) - 1);
// trying to reset SIGTSTP handler to default and raise it will
// result in a race and there's no other way to invoke the
// default handler. so just invoke SIGSTOP since it's
// effectively the same thing.
raise(SIGSTOP);
} else if (c == PIPE_CONT) {
getch2_poll(); getch2_poll();
} }
}
if (fds[2].revents) { if (fds[2].revents) {
int retval = read(tty_in, &buf.b[buf.len], BUF_LEN - buf.len); int retval = read(tty_in, &buf.b[buf.len], BUF_LEN - buf.len);
if (!retval || (retval == -1 && errno != EINTR && errno != EAGAIN && errno != EIO)) if (!retval || (retval == -1 && errno != EINTR && errno != EAGAIN && errno != EIO))
@ -566,8 +559,8 @@ void terminal_init(void)
tcgetattr(tty_in, &tio_orig); tcgetattr(tty_in, &tio_orig);
// handlers to fix terminal settings // handlers to fix terminal settings
setsigaction(SIGCONT, continue_sighandler, 0, true); setsigaction(SIGCONT, stop_cont_sighandler, 0, true);
setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); setsigaction(SIGTSTP, stop_cont_sighandler, 0, true);
setsigaction(SIGTTIN, SIG_IGN, 0, true); setsigaction(SIGTTIN, SIG_IGN, 0, true);
setsigaction(SIGTTOU, SIG_IGN, 0, true); setsigaction(SIGTTOU, SIG_IGN, 0, true);