From adb84293abfc1c3349ab34c03b036f1756e89aca Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Tue, 21 Mar 2023 15:30:54 -0600
Subject: [PATCH] Move updating of the window size back to the main sudo
 process. We can use the leader file descriptor with TIOCGWINSZ to set the
 window size of the pty.  Thanks to Duncan Overbruck for the hint.


Reference:https://github.com/sudo-project/sudo/commit/adb84293abfc1c3349ab34c03b036f1756e89aca
Conflict:src/exec_monitor.c src/exec_pty.c

---
 src/exec_monitor.c | 60 ++++++----------------------------------------
 src/exec_pty.c     | 23 +++++++++---------
 src/sudo.h         |  1 -
 3 files changed, 18 insertions(+), 66 deletions(-)

diff --git a/src/exec_monitor.c b/src/exec_monitor.c
index a92ed4099..2f2e0bd83 100644
--- a/src/exec_monitor.c
+++ b/src/exec_monitor.c
@@ -24,7 +24,6 @@
 #include <config.h>
 
 #include <sys/types.h>
-#include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <stdio.h>
@@ -35,7 +34,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
-#include <termios.h>
 
 #include "sudo.h"
 #include "sudo_exec.h"
@@ -62,8 +60,6 @@ struct monitor_closure {
     struct sudo_event *sigchld_event;
 };
 
-static bool tty_initialized;
-
 /*
  * Deliver a signal to the running command.
  * The signal was either forwarded to us by the parent sudo process
@@ -103,11 +99,6 @@ deliver_signal(struct monitor_closure *mc, int signo, bool from_parent)
 		"%s: unable to set foreground pgrp to %d (command)",
 		__func__, (int)mc->cmnd_pgrp);
 	}
-	/* Lazily initialize the pty if needed. */
-	if (!tty_initialized) {
-	    if (sudo_term_copy(io_fds[SFD_USERTTY], io_fds[SFD_FOLLOWER]))
-		    tty_initialized = true;
-	}
 	killpg(mc->cmnd_pid, SIGCONT);
 	break;
     case SIGCONT_BG:
@@ -130,34 +121,6 @@ deliver_signal(struct monitor_closure *mc, int signo, bool from_parent)
     debug_return;
 }
 
-/*
- * Unpack rows and cols from a CMD_TTYWINCH value, set the new window
- * size on the pty follower and inform the command of the change.
- */
-static void
-handle_winch(struct monitor_closure *mc, unsigned int wsize_packed)
-{
-    struct winsize wsize, owsize;
-    debug_decl(handle_winch, SUDO_DEBUG_EXEC);
-
-    /* Rows and columns are stored as two shorts packed into a single int. */
-    wsize.ws_row = wsize_packed & 0xffff;
-    wsize.ws_col = (wsize_packed >> 16) & 0xffff;
-
-    if (ioctl(io_fds[SFD_FOLLOWER], TIOCGWINSZ, &owsize) == 0 &&
-	(wsize.ws_row != owsize.ws_row || wsize.ws_col != owsize.ws_col)) {
-
-	sudo_debug_printf(SUDO_DEBUG_INFO,
-	    "window size change %dx%d -> %dx%d",
-	    owsize.ws_col, owsize.ws_row, wsize.ws_col, wsize.ws_row);
-
-	(void)ioctl(io_fds[SFD_FOLLOWER], TIOCSWINSZ, &wsize);
-	deliver_signal(mc, SIGWINCH, true);
-    }
-
-    debug_return;
-}
-
 /*
  * Send status to parent over socketpair.
  * Return value is the same as send(2).
@@ -368,16 +331,11 @@ mon_backchannel_cb(int fd, int what, void *v)
 	mc->cstat->val = n ? EIO : ECONNRESET;
 	sudo_ev_loopbreak(mc->evbase);
     } else {
-	switch (cstmp.type) {
-	case CMD_TTYWINCH:
-	    handle_winch(mc, cstmp.val);
-	    break;
-	case CMD_SIGNO:
+	if (cstmp.type == CMD_SIGNO) {
 	    deliver_signal(mc, cstmp.val, true);
-	    break;
-	default:
-	    sudo_warnx(U_("unexpected reply type on backchannel: %d"), cstmp.type);
-	    break;
+	} else {
+	    sudo_warnx(U_("unexpected reply type on backchannel: %d"),
+		cstmp.type);
 	}
     }
     debug_return;
@@ -556,9 +514,11 @@ exec_monitor(struct command_details *details, sigset_t *oset,
     int errpipe[2];
     debug_decl(exec_monitor, SUDO_DEBUG_EXEC);
 
-    /* The pty leader is not used by the monitor. */
+    /* Close fds the monitor doesn't use. */
     if (io_fds[SFD_LEADER] != -1)
 	close(io_fds[SFD_LEADER]);
+    if (io_fds[SFD_USERTTY] != -1)
+	close(io_fds[SFD_USERTTY]);
 
     /* Ignore any SIGTTIN or SIGTTOU we receive (shouldn't be possible). */
     memset(&sa, 0, sizeof(sa));
@@ -570,10 +530,6 @@ exec_monitor(struct command_details *details, sigset_t *oset,
     if (sudo_sigaction(SIGTTOU, &sa, NULL) != 0)
 	sudo_warn(U_("unable to set handler for signal %d"), SIGTTOU);
 
-    /* If we are starting in the foreground, the pty was already initialized. */
-    if (foreground)
-	tty_initialized = true;
-
     /*
      * Start a new session with the parent as the session leader
      * and the follower device as the controlling terminal.
@@ -630,8 +586,6 @@ exec_monitor(struct command_details *details, sigset_t *oset,
 	sigprocmask(SIG_SETMASK, oset, NULL);
 	close(backchannel);
 	close(errpipe[0]);
-	if (io_fds[SFD_USERTTY] != -1)
-	    close(io_fds[SFD_USERTTY]);
 	restore_signals();
 
 	/* setup tty and exec command */
diff --git a/src/exec_pty.c b/src/exec_pty.c
index 9a8ddfab1..e764564aa 100644
--- a/src/exec_pty.c
+++ b/src/exec_pty.c
@@ -59,6 +59,7 @@ static struct monitor_message_list monitor_messages =
 static char ptyname[PATH_MAX];
 int io_fds[6] = { -1, -1, -1, -1, -1, -1};
 static bool foreground, pipeline;
+static bool tty_initialized;
 static int ttymode = TERM_COOKED;
 static sigset_t ttyblock;
 static struct io_buffer_list iobufs;
@@ -159,6 +160,10 @@ check_foreground(struct exec_closure *ec)
     if (io_fds[SFD_USERTTY] != -1) {
 	if ((ret = tcgetpgrp(io_fds[SFD_USERTTY])) != -1) {
 	    foreground = ret == ec->ppgrp;
+	    if (foreground && !tty_initialized) {
+		if (sudo_term_copy(io_fds[SFD_USERTTY], io_fds[SFD_FOLLOWER]))
+		    tty_initialized = true;
+	    }
 
 	    /* Also check for window size changes. */
 	    sync_ttysize(ec);
@@ -840,11 +845,6 @@ fwdchannel_cb(int sock, int what, void *v)
 	    sudo_debug_printf(SUDO_DEBUG_INFO,
 		"sending SIG%s to monitor over backchannel", signame);
 	    break;
-	case CMD_TTYWINCH:
-	    sudo_debug_printf(SUDO_DEBUG_INFO, "sending window size change "
-		"to monitor over backchannelL %d x %d",
-		msg->cstat.val & 0xffff, (msg->cstat.val >> 16) & 0xffff);
-	    break;
 	default:
 	    sudo_debug_printf(SUDO_DEBUG_INFO,
 		"sending cstat type %d, value %d to monitor over backchannel",
@@ -1192,6 +1192,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
 		if (sudo_term_raw(io_fds[SFD_USERTTY], 0))
 		    ttymode = TERM_RAW;
 	    }
+	    tty_initialized = true;
 	}
     }
 
@@ -1347,8 +1348,8 @@ exec_pty(struct command_details *details, struct command_status *cstat)
 }
 
 /*
- * Check for tty size changes.
- * Passes the new window size to the I/O plugin and to the monitor.
+ * Propagate tty size change to pty being used by the command, pass
+ * new window size to I/O plugins and deliver SIGWINCH to the command.
  */
 static void
 sync_ttysize(struct exec_closure_pty *ec)
@@ -1358,14 +1359,12 @@ sync_ttysize(struct exec_closure_pty *ec)
 
     if (ioctl(io_fds[SFD_USERTTY], TIOCGWINSZ, &wsize) == 0) {
 	if (wsize.ws_row != ec->rows || wsize.ws_col != ec->cols) {
-	    const unsigned int wsize_packed = (wsize.ws_row & 0xffff) |
-		((wsize.ws_col & 0xffff) << 16);
-
 	    /* Log window change event. */
 	    log_winchange(wsize.ws_row, wsize.ws_col);
 
-	    /* Send window change event to monitor process. */
-	    send_command_status(ec, CMD_TTYWINCH, wsize_packed);
+	    /* Update pty window size and send command SIGWINCH. */
+	    (void)ioctl(io_fds[SFD_LEADER], TIOCSWINSZ, &wsize);
+	    killpg(ec->cmnd_pid, SIGWINCH);
 
 	    /* Update rows/cols. */
 	    ec->rows = wsize.ws_row;
diff --git a/src/sudo.h b/src/sudo.h
index acb89b751..204c3e76e 100644
--- a/src/sudo.h
+++ b/src/sudo.h
@@ -225,7 +225,6 @@ struct command_status {
 #define CMD_WSTATUS	2
 #define CMD_SIGNO	3
 #define CMD_PID		4
-#define CMD_TTYWINCH	5
     int type;
     int val;
 };
-- 
2.33.0

