Uploaded image for project: 'RHEL'
  1. RHEL
  2. RHEL-5723

sudo cannot restore terminal settings, causing ksh to break

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Normal Normal
    • None
    • rhel-8.7.0, rhel-9.2.0, rhel-10.0
    • ksh
    • None
    • Moderate
    • rhel-sst-cs-plumbers
    • ssg_core_services
    • 5
    • False
    • Hide

      None

      Show
      None
    • None
    • None
    • None
    • None
    • If docs needed, set a value
    • None

      Description of problem:

      When sudo is configured with "Defaults use_pty", executing the following command from a ksh shell breaks ksh:
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      $ sudo -u root echo hello|head -1
      [sudo] password for kshuser:
      hello
      $
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      At the prompt, which is also not properly printed (should be at beginning of the line), typing keys shows nothing, because the terminal remains in "noecho" mode forever.

      The root cause for this is "sudo" fails to restore the "echo" mode, due to getting SIGTTOU:
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      32666 15:26:03.848421 ioctl(4</dev/tty<char 5:0>>, SNDCTL_TMR_STOP or TCSETSW,

      {c_iflags=0x4500, c_oflags=0x5, c_cflags=0xbf, c_lflags=0x8a3b, c_line=0, c_cc="\x03\x1c\x7f\x15\x04\x00\x01\x00\x11\x13\x1a\xff\x12\x0f\x17\x16\xff\x00\x00"}

      ) = ? ERESTARTSYS (To be restarted if SA_RESTART is set) <0.000007>
      32666 15:26:03.848448 — SIGTTOU

      {si_signo=SIGTTOU, si_code=SI_KERNEL}


      32666 15:26:03.848458 rt_sigreturn(

      {mask=[]}

      ) = -1 EINTR (Interrupted system call) <0.000003>
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      Corresponding "sudo" source code is:
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      119 static int
      120 tcsetattr_nobg(int fd, int flags, struct termios *tp)
      121 {
      122 struct sigaction sa, osa;
      123 int rc;
      124
      125 /*
      126 * If we receive SIGTTOU from tcsetattr() it means we are
      127 * not in the foreground process group.
      128 * This should be less racy than using tcgetpgrp().
      129 */
      130 memset(&sa, 0, sizeof(sa));
      131 sigemptyset(&sa.sa_mask);
      132 sa.sa_handler = sigttou;
      133 got_sigttou = 0;
      134 sigaction(SIGTTOU, &sa, &osa);
      135 do

      { 136 rc = tcsetattr(fd, flags, tp); 137 }

      while (rc != 0 && errno == EINTR && !got_sigttou);
      138 sigaction(SIGTTOU, &osa, NULL);
      139
      140 return rc;
      141 }

      147 bool
      148 sudo_term_restore_v1(int fd, bool flush)
      149 {
      150 debug_decl(sudo_term_restore, SUDO_DEBUG_UTIL)
      151
      152 if (changed)

      { 153 const int flags = flush ? (TCSASOFT|TCSAFLUSH) : (TCSASOFT|TCSADRAIN); 154 if (tcsetattr_nobg(fd, flags, &oterm) != 0) 155 debug_return_bool(false); 156 changed = 0; 157 }

      158 debug_return_bool(true);
      159 }
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      Here above on line 154, the call fails because tcsetattr_nobg() gets SIGTTOU signal.

      This happens because "sudo" is not the foreground process anymore, it's "ksh" which took over already:
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      32627 15:26:03.847559 ioctl(2</dev/pts/1<char 136:1>>, TIOCGPGRP <unfinished ...>
      32627 15:26:03.847584 <... ioctl resumed>, [32666]) = 0 <0.000013>

      --> here 32666 is "sudo", which makes "ksh" take over foreground:

      32627 15:26:03.847604 ioctl(2</dev/pts/1<char 136:1>>, TIOCSPGRP, [32627] <unfinished ...>
      32627 15:26:03.847627 <... ioctl resumed>) = 0 <0.000012>
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      We can see using a stap script that "ksh" gets back to foreground while waiting for "sudo" to exit:
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      32725 (ksh) calling ioctl(TIOCSPGRP)
      0x7fb819f127cb : ioctl+0xb/0x30 [/usr/lib64/libc-2.28.so]
      0x7fb819fff48d : tcsetpgrp+0x1d/0x30 [/usr/lib64/libc-2.28.so]
      0x56259cc8778a : job_reset+0x9a/0xc0 [/usr/bin/ksh93]
      0x56259cc8965c : job_wait+0x30c/0x8a0 [/usr/bin/ksh93]
      0x56259ccbe70a : sh_exec+0x424a/0x5cf0 [/usr/bin/ksh93]
      0x56259ccbd880 : sh_exec+0x33c0/0x5cf0 [/usr/bin/ksh93]
      0x56259cc66327 : exfile+0x977/0xcd0 [/usr/bin/ksh93]
      0x56259cc66c3c : sh_main+0x36c/0x930 [/usr/bin/ksh93]
      0x7fb819f13d85 : __libc_start_main+0xe5/0x180 [/usr/lib64/libc-2.28.so]
      0x56259cc658ee : _start+0x2e/0x30 [/usr/bin/ksh93]
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      Which is the following "ksh" code:
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      1486 int job_wait(register pid_t pid)
      1487 {
      :

      1549 while(1)
      1550 {
      :
      1631 }
      :

      1641 if(pw->p_pgrp)
      1642 {
      1643 job_reset(pw);
      :
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      Delaying line 1643 until sudo really completed shows terminal is functional again.

      It looks like to me the "while(1)" block (lines 1549-1631) is not waiting for all jobs to complete.

      I can see using gdb that the block exits because "ksh" monitors "head" which exits before "sudo":
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
      (gdb) break jobs.c:1614
      Breakpoint 2 at 0x55db5ac8e5fc: file /usr/src/debug/ksh-20120801-257.el8.x86_64/src/cmd/ksh93/sh/jobs.c, line 1614.
      (gdb) cont

      Breakpoint 2, job_wait (pid=34776) at /usr/src/debug/ksh-20120801-257.el8.x86_64/src/cmd/ksh93/sh/jobs.c:1614
      1614 if(!px || !job.waitall)
      (gdb) p pw->p_pid
      $3 = 34776
      (gdb) p pw->p_nxtproc
      $4 = (struct process *) 0x7f4b95ec1a60
      (gdb) p pw->p_nxtproc->p_pid
      $5 = 34775

      (gdb) p job.waitall
      $6 = 0 '\000'
      -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

      Because we don't have "job.waitall", we exit the block and "hijack" the terminal through going foreground again, without letting "sudo" restore the settings.

      Version-Release number of selected component (if applicable):

      ksh-20120801-257.el8
      sudo-1.8.29-8.el8_7.1

      How reproducible:

      Always

      Steps to Reproduce:
      1. Add "Defaults use_pty" in /etc/sudoers
      2. As a KSH user, execute the above command

              vmihalko Vincent Mihalkovic
              rhn-support-rmetrich Renaud Métrich
              Vincent Mihalkovic Vincent Mihalkovic
              Karel Volný Karel Volný
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated: