Skip site navigation (1)Skip section navigation (2)
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>