Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 5 Apr 1999 18:08:45 -0600 (MDT)
From:      Stephen Clawson <sclawson@cs.utah.edu>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/10970: Children of ypserv don't necessarily go away.
Message-ID:  <199904060008.SAA19667@ibapah.cs.utah.edu>

next in thread | raw e-mail | index | archive | help

>Number:         10970
>Category:       bin
>Synopsis:       Children of ypserv don't necessarily go away.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr  5 17:10:01 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Stephen Clawson
>Release:        FreeBSD 3.0-CURRENT i386 (jan 27, 1999)
>Organization:
University of Utah Computer Science
>Environment:

	We've set up a yp slave server on a dual Pentium II/350 to
provide yp service to a group of twenty or so FreeBSD machines and a
few assorted linux boxes.  I actually noticed this bug while trying to 
track down why ypserv was segfaulting on us. =)  The underlying
network is switched 100BaseT to the server.

>Description:

There are only two places that ypserv forks: the yp_all handling code
(ypproc_all_2_svc) and yp_xfr (ypproc_xfr_2_svc).  In either case, the
child sets a flag (forked) so that when it returns to the main loop it
will exit. (code from yp_main.c:yp_svc_run):

               if (forked && pid != getpid())
                        _exit(0);

The problem is that along with setting the flag in the child, it
resets the flag in the parent!  This causes problems, since you can
potentially service multiple requests in the child, as evicenced by a
backtrace I captured from a forked child (I added debugging code to
stop the process so that I could attach to it if it entered
pproc_all_2_svc again after it had finished):

(gdb) bt
#0  0x8049050 in ypproc_all_2_svc (argp=0xefbfc89c, rqstp=0xefbfcda4)
    at yp_server.c:527
#1  0x804862a in ypprog_2 (rqstp=0xefbfcda4, transp=0x8088140) at
yp_svc.c:265
#2  0x804d54e in svc_getreqset2 (readfds=0x8086080, width=18)
    at /usr/src/lib/libc/../libc/rpc/svc.c:465
#3  0x804bd6e in readtcp (xprt=0x8088380, buf=0x80a6fa0 "\200",
len=4000)
    at /usr/src/lib/libc/../libc/rpc/svc_tcp.c:351
#4  0x8059ccb in fill_input_buf (rstrm=0x808d500)
    at /usr/src/lib/libc/../libc/xdr/xdr_rec.c:511
#5  0x8059d0c in get_input_bytes (rstrm=0x808d500,
    addr=0xefbfce90 "\005\b/libc/xdr/xdr_rec.c:212
#9  0x805d05c in xdr_u_int32_t (xdrs=0x809d808, u_int32_p=0xefbfd430)
    at /usr/src/lib/libc/../libc/xdr/xdr.c:242
#10 0x804d8fd in xdr_callmsg (xdrs=0x809d808, cmsg=0xefbfd430)
    at /usr/src/lib/libc/../libc/rpc/rpc_callmsg.c:180
#11 0x804bed6 in svctcp_recv (xprt=0x8088380, msg=0xefbfd430)
    at /usr/src/lib/libc/../libc/rpc/svc_tcp.c:421
#12 0x804d472 in svc_getreqset2 (readfds=0xefbfd494, width=1024)
    at /usr/src/lib/libc/../libc/rpc/svc.c:442
#13 0x804d3c8 in svc_getreqset (readfds=0xefbfd494)
    at /usr/src/lib/libc/../libc/rpc/svc.c:408
#14 0x804ae10 in yp_svc_run () at yp_main.c:146
#15 0x804b2b4 in main (argc=1, argv=0xefbfd570) at yp_main.c:339
#16 0x80480ca in _start ()

This shouldn't be a suprise, since according to the man page,
svc_getreqset() "returns when all sockets associated with the value of
rdfds have been serviced."  When the child goes through the second
time it's now a parent and so it's forked flag is reset!  Thus when it
does eventually return it dosen't go away. =(

>How-To-Repeat:

Since calling yp_all is the only procedure that will cause this
problem, it's pretty easy to reproduce.  Set up a yp server and
barrage it with as many yp_all requests that you can.  The simplest
way to do this is to fork off a bunch of `ypcat passwd' processes from
a client machine (20 is usually sufficent).

You've got to get enough going at a time that a forked child will
respond to two or more.   

>Fix:

Assuming that it's reasonable to have a forked child respond to
multiple requests(*), then the simplest thing to do is to get rid of the
code that resets `forked' in the parent process.  We'll also want to
make sure that the child dosen't fork again, since it's stupid to fork
off another process to handle a request when you're just going to
return and exit...  It also screws up the code that's trying to limit
the maximum number of children if child processes can fork. =)

The pid gunk and the change from exit(0) to _exit(0) should also go
away, since they seem to be trying to fix the same problem, but don't
make any sense from what I can tell.

(*) In OpenBSD's version of ypserv, the child that handled yp_all just
    exits after it's done instead of returning.  This also takes care
    of the problem.


Index: yp_main.c
===================================================================
RCS file:
/n/marker/usr/lsrc/FreeBSD/CVS/src/usr.sbin/ypserv/yp_main.c,v
retrieving revision 1.19
diff -c -r1.19 yp_main.c
*** yp_main.c   1999/02/10 16:16:14     1.19
--- yp_main.c   1999/04/03 00:28:27
***************
*** 108,120 ****
        int readfds;
  #endif /* def FD_SETSIZE */
        extern int forked;
-       int pid;
        int fd_setsize = _rpc_dtablesize();
        struct timeval timeout;
  
-       /* Establish the identity of the parent ypserv process. */
-       pid = getpid();
- 
        for (;;) {
  #ifdef FD_SETSIZE
                readfds = svc_fdset;
--- 108,116 ----
***************
*** 144,151 ****
                        }
                        svc_getreqset(&readfds);
                }
!               if (forked && pid != getpid())
!                       _exit(0);
        }
  }
  
--- 140,147 ----
                        }
                        svc_getreqset(&readfds);
                }
!               if (forked) 
!                       exit(0);
        }
  }
  
Index: yp_server.c
===================================================================
RCS file:
/n/marker/usr/lsrc/FreeBSD/CVS/src/usr.sbin/ypserv/yp_server.c,v
retrieving revision 1.27
diff -c -r1.27 yp_server.c
*** yp_server.c 1999/02/10 16:16:14     1.27
--- yp_server.c 1999/04/03 00:25:38
***************
*** 392,398 ****
        default:
                result.xfrstat = YPXFR_SUCC;
                children++;
-               forked = 0;
                break;
        }
  
--- 392,397 ----
***************
*** 506,512 ****
         * block. (Is there a better way to do this? Maybe with
         * async socket I/O?)
         */
!       if (!debug) {
                switch(fork()) {
                case 0:
                        forked++;
--- 505,511 ----
         * block. (Is there a better way to do this? Maybe with
         * async socket I/O?)
         */
!       if (!forked && !debug) {
                switch(fork()) {
                case 0:
                        forked++;
***************
*** 518,524 ****
                        break;
                default:
                        children++;
-                       forked = 0;
                        return (NULL);
                        break;
                }
--- 517,522 ----








To: FreeBSD-gnats-submit@freebsd.org
Subject: 
From: sclawson
Reply-To: sclawson
X-send-pr-version: 3.2


>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?199904060008.SAA19667>