From: Karel Zak Date: Tue, 28 Jul 2026 12:16:31 +0000 (+0200) Subject: pty-session: defer raw mode until after signals are blocked X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=288b503e6f2f02fcfda10ff718e85b2367460cf0;p=thirdparty%2Futil-linux.git pty-session: defer raw mode until after signals are blocked ul_pty_setup() switches the user's terminal to raw mode before ul_pty_signals_setup() blocks signals. A terminating signal delivered in this window kills the process without reaching ul_pty_cleanup(), leaving the terminal in raw mode. Move the raw-mode tcsetattr() out of ul_pty_setup() into a new ul_pty_terminal_setup() function, to be called after ul_pty_signals_setup(). This ensures signals are already directed to signalfd when raw mode is activated, closing the race window. The setup sequence is now: ul_pty_setup() -- open pty, save terminal attrs -- e.g. utempter (needs SIGCHLD unblocked) ul_pty_signals_setup() -- block signals, create signalfd ul_pty_terminal_setup() -- set raw mode (signals already blocked) fork() Addresses: https://github.com/util-linux/util-linux/issues/4499 Signed-off-by: Karel Zak --- diff --git a/include/pty-session.h b/include/pty-session.h index af54197b3..a91db2af6 100644 --- a/include/pty-session.h +++ b/include/pty-session.h @@ -108,6 +108,7 @@ struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty); int ul_pty_is_running(struct ul_pty *pty); int ul_pty_setup(struct ul_pty *pty); int ul_pty_signals_setup(struct ul_pty *pty); +int ul_pty_terminal_setup(struct ul_pty *pty); void ul_pty_cleanup(struct ul_pty *pty); int ul_pty_chownmod_slave(struct ul_pty *pty, uid_t uid, gid_t gid, mode_t mode); void ul_pty_init_slave(struct ul_pty *pty); diff --git a/lib/pty-session.c b/lib/pty-session.c index 12d7326a2..d72ea6c42 100644 --- a/lib/pty-session.c +++ b/lib/pty-session.c @@ -186,9 +186,6 @@ int ul_pty_setup(struct ul_pty *pty) if (rc) goto done; - /* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */ - cfmakeraw(&attrs); - tcsetattr(STDIN_FILENO, TCSANOW, &attrs); } else { DBG_OBJ(SETUP, pty, ul_debug("create for non-terminal")); @@ -217,6 +214,23 @@ done: return rc; } +/* call me after ul_pty_signals_setup() and before fork() */ +int ul_pty_terminal_setup(struct ul_pty *pty) +{ + struct termios attrs; + + if (!pty->isterm) + return 0; + + attrs = pty->stdin_attrs; + cfmakeraw(&attrs); + if (tcsetattr(STDIN_FILENO, TCSANOW, &attrs)) + return -errno; + + DBG_OBJ(SETUP, pty, ul_debug("terminal raw mode setup done")); + return 0; +} + /* call me before fork() */ int ul_pty_signals_setup(struct ul_pty *pty) { @@ -806,6 +820,8 @@ int main(int argc, char *argv[]) err(EXIT_FAILURE, "failed to create pseudo-terminal"); if (ul_pty_signals_setup(pty)) err(EXIT_FAILURE, "failed to initialize signals handler"); + if (ul_pty_terminal_setup(pty)) + err(EXIT_FAILURE, "failed to setup terminal"); fflush(stdout); /* ??? */ diff --git a/login-utils/su-common.c b/login-utils/su-common.c index c4b30f39a..2e349e82e 100644 --- a/login-utils/su-common.c +++ b/login-utils/su-common.c @@ -564,6 +564,10 @@ static void create_watching_parent(struct su_context *su) supam_cleanup(su, PAM_ABORT); err(EXIT_FAILURE, _("failed to initialize signals handler")); } + if (ul_pty_terminal_setup(su->pty)) { + supam_cleanup(su, PAM_ABORT); + err(EXIT_FAILURE, _("failed to setup terminal")); + } } #endif fflush(stdout); /* ??? */ diff --git a/term-utils/script.c b/term-utils/script.c index 3991ea646..d84b61111 100644 --- a/term-utils/script.c +++ b/term-utils/script.c @@ -1027,6 +1027,8 @@ int main(int argc, char **argv) if (ul_pty_signals_setup(ctl.pty)) err(EXIT_FAILURE, _("failed to initialize signals handler")); + if (ul_pty_terminal_setup(ctl.pty)) + err(EXIT_FAILURE, _("failed to setup terminal")); fflush(stdout); /* diff --git a/term-utils/scriptlive.c b/term-utils/scriptlive.c index bf7c31244..95bcbb5c7 100644 --- a/term-utils/scriptlive.c +++ b/term-utils/scriptlive.c @@ -305,6 +305,8 @@ main(int argc, char *argv[]) err(EXIT_FAILURE, _("failed to create pseudo-terminal")); if (ul_pty_signals_setup(ss.pty)) err(EXIT_FAILURE, _("failed to initialize signals handler")); + if (ul_pty_terminal_setup(ss.pty)) + err(EXIT_FAILURE, _("failed to setup terminal")); fflush(stdout); /* ??? */