]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
pty-session: defer raw mode until after signals are blocked
authorKarel Zak <kzak@redhat.com>
Tue, 28 Jul 2026 12:16:31 +0000 (14:16 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 28 Jul 2026 12:16:31 +0000 (14:16 +0200)
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
  <app-specific work>     -- 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 <kzak@redhat.com>
include/pty-session.h
lib/pty-session.c
login-utils/su-common.c
term-utils/script.c
term-utils/scriptlive.c

index af54197b329bf39e89d383eb4299ea749a614719..a91db2af62af86f6e3a21cd2ec3b8c6521482e8f 100644 (file)
@@ -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);
index 12d7326a2ab0510d87eb321a0b237be0be0a72df..d72ea6c42b33d93857e4c46a5a12bd366fe37895 100644 (file)
@@ -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);                 /* ??? */
 
index c4b30f39aa83f0b08e1195c2b844718f3c468193..2e349e82e127753e28454d3065967cb78e3b1faf 100644 (file)
@@ -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);                 /* ??? */
index 3991ea6461fa2678f6ddeeabaa1653d9a9881af0..d84b61111773e8b84e9ea43ac9ec2a7f470a1b7e 100644 (file)
@@ -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);
 
        /*
index bf7c31244f93a75a38212b37013a9aab18958b7d..95bcbb5c70392ec859a07871751518daea35ebcc 100644 (file)
@@ -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);                 /* ??? */