Date: Fri, 7 Jul 2000 22:42:10 -0700 (PDT) From: joelh@gnu.org To: FreeBSD-gnats-submit@freebsd.org Subject: bin/19773: [PATCH] telnet infinite loop depending on how fds are closed Message-ID: <200007080542.WAA07069@beastie.mayfield.hp.com>
next in thread | raw e-mail | index | archive | help
>Number: 19773 >Category: bin >Synopsis: [PATCH] telnet infinite loop depending on how fds are closed >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Fri Jul 07 19:00:01 PDT 2000 >Closed-Date: >Last-Modified: >Originator: Joel Ray Holveck >Release: FreeBSD 4.0-STABLE i386 >Organization: none >Environment: FreeBSD 4.0, Pentium 90, nothing extraordinary here that seems relevant >Description: The telnet client can get into an infinite loop if the pipe going to stdout and stderr is closed before the pipe going to stdin. This is because, on a SIGPIPE, it will assume that the remote has closed its connection. It longjmp()'s out of the signal handler, and reports this to the user on stdout or stderr (not sure which, doesn't really matter), generating another SIGPIPE. Now, that SIGPIPE will try the same longjmp. telnet tries to report again, gets another SIGPIPE... ad infinitum. >How-To-Repeat: Compile and run the following program. (I'm not sure how necessary the sleeps are, and this has a few minor bugs, but it demonstrates the problem.) After the process closes the pipe from "me->telnet", then the telnet process (find it with top or ps, sorry) will go into the aforementioned infinite loop. Note that the telnet sticks around after the calling program exits; find it with top or ps and kill it yourself. #include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { int topipe[2]; int frompipe[2]; pipe(&topipe); pipe(&frompipe); if (!fork()) { close(topipe[1]); close(frompipe[0]); dup2(topipe[0], 0); dup2(frompipe[1], 1); dup2(frompipe[1], 2); execlp("/usr/bin/telnet", "telnet", "localhost", NULL); abort(); } close(topipe[0]); close(frompipe[1]); printf("forked\n"); sleep(5); printf("closing telnet->me\n"); close(frompipe[0]); sleep(3); printf("closing me->telnet\n"); close(topipe[1]); sleep(2); printf("bye\n"); exit(0); } >Fix: Here's the fix for crypto/telnet/telnet/sys_bsd.c. I haven't looked at the other telnets, but I would assume they would be similar. Caveat: I've tested this precisely twice. --- sys_bsd.c.orig Fri Jul 7 21:35:57 2000 +++ sys_bsd.c Fri Jul 7 21:50:59 2000 @@ -941,9 +941,17 @@ void sys_telnet_init() { + struct sigaction act; (void) signal(SIGINT, intr); (void) signal(SIGQUIT, intr2); - (void) signal(SIGPIPE, deadpeer); + /* We only want deadpeer to be called once, 'cause if there's a broken + * pipe on stdout, we can't handle a SIGPIPE while reporting the + * dropped connection. + */ + act.sa_handler = deadpeer; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESETHAND; + (void) sigaction(SIGPIPE, &act, NULL); #ifdef SIGWINCH (void) signal(SIGWINCH, sendwin); #endif >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200007080542.WAA07069>