Bug 59029 - in.telnetd creating runaway processes upon closing
in.telnetd creating runaway processes upon closing
Status: CLOSED RAWHIDE
Product: Red Hat Linux
Classification: Retired
Component: util-linux (Show other bugs)
7.2
i686 Linux
medium Severity medium
: ---
: ---
Assigned To: Elliot Lee
Ben Levenson
:
: 59411 62518 89653 (view as bug list)
Depends On:
Blocks: 86557
  Show dependency treegraph
 
Reported: 2002-01-29 11:25 EST by Need Real Name
Modified: 2007-04-18 12:39 EDT (History)
4 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2002-03-08 14:03:32 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)

  None (edit)
Description Need Real Name 2002-01-29 11:25:10 EST
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:0.9.7) Gecko/20011226

Description of problem:
when a telnet client is killed, the telnet server does not kill a child process
which is sleeping in a select() call

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


How reproducible:
Always

Steps to Reproduce:
1. Compile the following with gcc.  This program just calls select() every 5
seconds.

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
	fd_set rfds;
	struct timeval tv;

	while (1) {
		FD_ZERO(&rfds);
		FD_SET(0, &rfds);
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		select(1, &rfds, NULL, NULL, &tv);
	}
	return 0;
}



2. With the telnet server enabled, do a 'xterm -e telnet localhost' and login.

3. In the new xterm window, start the program compiled in step 1.

4. kill the new xterm window

5. run top/ps and see the runaway process in an endless loop 	

Actual Results:  The sample program that was run from within the telnet session
will not be killed.  It will take up 100% of the cpu on a single cpu box.

Expected Results:  The sample program should have been killed

Additional info:

This problem is not seen when using ssh or when starting the sample program
directly.
Comment 1 Harald Hoyer 2002-02-28 06:18:01 EST
The pipe was closed... stating the german man page :)
man 2 select
       Nachdem der  einzige  Schreiber  eine  named  Pipe  schlie_t,  wird
       select()  zur|ckkehren  und angeben, da_ etwas von der Pipe gelesen
       werden kann.  Wenn dann von dieser gelesen  wird,  wird  read(2)  0
       zur|ckliefern,  was das Dateiende markiert.  Code, der annimmt, da_
       select() blockiert, sollte  die  Pipe  mit  O_RDWR  statt  O_RDONLY
       vffnen.

After the only writer closed a named pipe, select() will return and indicate
that s.th. from the pipe can be read. If you read from the pipe, read(2) will
return 0, which indicates the end of file. Code, which wants that select() 
blocks should open the pipe with O_RDWR instead of O_RDONLY.
Comment 2 Need Real Name 2002-03-01 11:38:31 EST
    The sample program was running select() on stdin.  Are you saying that to
avoid this problem, a programmer must close and reopen stdin before attempting
to to use a select() on it ?!?  This is not a satisfactory solution.
Comment 3 Harald Hoyer 2002-03-01 12:59:17 EST
i am saying that stdin has EOF and cannot be reopened (no more terminal)
Comment 4 Need Real Name 2002-03-01 13:40:10 EST
I see your point.  Where we are seeing this issue is inside the runtime of
rmcobol.  The sample program was made by examining 'strace' logs (I believe that
in the logs there is read on stdin following the select).  Apparently rmcobol is
not looking for an end of file condition on stdin.

Is the sample program not receiving a signal upon the death of it's parent? Is
it ignoring it?



Comment 5 Need Real Name 2002-03-01 16:40:45 EST
I took a closer look.  I followed the instructions in the initial post I got the
following processes

$ps axj 

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  739  5351  5351  5351 ?           -1 S        0   0:00 in.telnetd: jm-linux
 5351  5352  5352  5352 pts/1     5401 S        0   0:00 login -- jmcelro9
 5352  5353  5353  5352 pts/1     5401 S    11277   0:00 -bash
 5353  5401  5401  5352 pts/1     5401 R    11277   0:19 ./go2

When a session leader exits (in this case login) a SIGHUP is supposed to be sent
to all the processes in it session.  This is what I expected to happen.

What appears to be happening is when telnetd dies, it is not signally its child
'login' process.  Because the login process is not dying, none of the processes
in its session are being sent the expect SIGHUP. hence the following:

$ps axj 

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1  5352  5352  5352 ?           -1 S        0   0:00 login -- jmcelro9
 5352  5353  5353  5352 ?           -1 S    11277   0:00 -bash
 5353  5401  5401  5352 ?           -1 R    11277   8:03 ./go2

If you replace the program in the initial post with one that does not check
stdin, you will run into same circumstance.  For instance the following code
will have the same problem:

int main(int argc, char *argv[]) {
     while(1) {
     }
     return 0;
}

as will the shell script 'while [ 1 ]; do a=$((a+1)); done'

Basically it looks like any non interactive program will continue to run after
the telnet session closes.
Comment 6 Harald Hoyer 2002-03-05 10:09:13 EST
ok, here is the problem:
- telnetd exits
- kernel sends SIGHUP to terminal sessions leader
- login has forked itsself because of PAM
- the parent login process ignores SIGHUP
- the child login (now bash) does not get SIGHUP
--> login and bash stay alive after telnetd exists

problem is in util-linux-2.11n-5 login.c(1135):
    } else if (childPid) {
       /* parent - wait for child to finish, then cleanup session */
       /* ioctl(0, TIOCNOTTY, NULL); */
       signal(SIGHUP, SIG_IGN);
       signal(SIGINT, SIG_IGN);
       signal(SIGQUIT, SIG_IGN);
       signal(SIGTERM, SIG_IGN);

       wait(NULL);
       PAM_END;
       exit(0);
    }

solution:
- catch the signal and kill(childPid, SIGHUP)
- or make the child the session leader (setsid())
Comment 7 Elliot Lee 2002-03-08 14:03:28 EST
*** Bug 59411 has been marked as a duplicate of this bug. ***
Comment 8 Elliot Lee 2002-03-11 15:00:47 EST
As per #54741, this is fixed in util-linux-2.11n-8
Comment 9 Harald Hoyer 2002-04-02 04:16:57 EST
*** Bug 62518 has been marked as a duplicate of this bug. ***
Comment 10 Harald Hoyer 2003-04-25 11:51:19 EDT
*** Bug 89653 has been marked as a duplicate of this bug. ***

Note You need to log in before you can comment on or make changes to this bug.