ul_pty_signals_setup() subscribes to SIGALRM through the signalfd set,
but handle_signal() has no case for it, so a SIGALRM delivered to the
proxy (e.g. an alarm(2) timer armed before execve(), or an external
"kill -ALRM") falls through to "default: abort()".
The abort() terminates script/su --pty/runuser --pty with SIGABRT before
ul_pty_cleanup() runs, leaving the user's terminal in raw mode (no echo,
no canonical mode, no signal generation) until "stty sane" is run.
Treat SIGALRM like the other terminating signals (SIGHUP/SIGTERM/SIGINT/
SIGQUIT): record it in delivered_signal and forward SIGTERM to the child
so the proxy leaves its poll() loop through the normal path and
ul_pty_cleanup() restores the saved terminal settings. The default
disposition of SIGALRM is termination, so this preserves the previous
outcome while adding the cleanup.
None of script, su --pty or runuser --pty arms SIGALRM internally on this
code path.
This addresses the deterministic SIGALRM path reported in #4499. The
separate, much narrower window where a terminating signal arrives
between ul_pty_setup() (raw mode on) and ul_pty_signals_setup() (signals
blocked) is not addressed here; closing it requires reworking the signal
mask contract between those two calls.
Partially fixes: https://github.com/util-linux/util-linux/issues/4499
Signed-off-by: lzwind <liuzheng@uniontech.com>
&info, (void *) &pty->win);
}
break;
+ case SIGALRM:
+ FALLTHROUGH;
case SIGHUP:
FALLTHROUGH;
case SIGTERM:
case SIGINT:
FALLTHROUGH;
case SIGQUIT:
- DBG_OBJ(SIG, pty, ul_debug(" get signal SIG{TERM,INT,QUIT}"));
+ DBG_OBJ(SIG, pty, ul_debug(" get signal SIG{ALRM,HUP,TERM,INT,QUIT}"));
pty->delivered_signal = info.ssi_signo;
/* Child termination is going to generate SIGCHLD (see above) */
if (pty->child > 0)