From: Karel Zak Date: Thu, 8 Mar 2007 21:21:15 +0000 (+0100) Subject: login: login's timeout can fail X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=649efbb369506a2096811186015b33a80cf15b94;p=util-linux login: login's timeout can fail 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 Signed-off-by: Karel Zak --- diff --git a/login-utils/login.c b/login-utils/login.c index 5895b8ef..ba3b86b0 100644 --- a/login-utils/login.c +++ b/login-utils/login.c @@ -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);