]> err.no Git - util-linux/commitdiff
login: login's timeout can fail
authorKarel Zak <kzak@redhat.com>
Thu, 8 Mar 2007 21:21:15 +0000 (22:21 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 8 Mar 2007 21:21:15 +0000 (22:21 +0100)
Login tries to set a timeout in main() by SIGALARM. If any restartable system
call is entered, such system calls can block indefinitely and will NOT be
interrupted by the SIGALRM.

The bug appears when the login program is run for a terminal for which it
doens't have read or write permission.

In that case, login hung until manually killed by the administrator in its
tcsetattr(...) call at login.c, line 460:
   /* Kill processes left on this tty */
tcsetattr(0,TCSAFLUSH,&ttt);

This may possibly be a kernel bug - instead of returning EIO / EPERM, the
kernel continously sends an infinite number of SIGTTOU signals to the process .

An 80MB strace log file was generated, consisting of >1,000,000 repetitions
of :
4964  11:00:18 ioctl(0, SNDCTL_TMR_CONTINUE or TCSETSF, {c_iflags=0x106,
c_oflags=0x1805, c_cflags=0x800000be, c_lflags=0x3b, c_line=0,
c_cc="\x03\x1c\x7f\x15\x04\x00\x01\x00\x11\x13\x1a\x00\x12\x0f\x17\x16\x00\x00\x00"})
= ? ERESTARTSYS (To be restarted)
4964  11:00:18 --- SIGTTOU (Stopped (tty output)) @ 0 (0) ---
4964  11:00:18 --- SIGTTOU (Stopped (tty output)) @ 0 (0) ---

Login's alarm signal handler DOES get the SIGALRM after the 60 second timeout,
and timedout() is called; but then timedout2 calls ioctl(0, TCSETA, &ti), which
also blocks, because the ioctl(0, TCSETSF...) of tcsetattr is in progress, and
the exit() call of timedout2 is never reached, and the tcsetattr call is
restarted.

From: Jason Vas Dias <jvdias@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
login-utils/login.c

index 5895b8efa1295873487dc1d453983f5fc386fdc1..ba3b86b095d1860502e5990d51cd245e174d8a87 100644 (file)
@@ -342,6 +342,7 @@ main(int argc, char **argv)
     pid = getpid();
 
     signal(SIGALRM, timedout);
+    siginterrupt(SIGALRM,1);           /* we have to interrupt syscalls like ioclt() */
     alarm((unsigned int)timeout);
     signal(SIGQUIT, SIG_IGN);
     signal(SIGINT, SIG_IGN);