Bug 7684

Summary: nis/ypclnt.c leaks file descriptors if ypbind is called
Product: [Retired] Red Hat Linux Reporter: Scott Sutherland <scott>
Component: glibcAssignee: Cristian Gafton <gafton>
Status: CLOSED RAWHIDE QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: 6.1   
Target Milestone: ---   
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2000-03-08 20:17:04 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Scott Sutherland 1999-12-08 16:44:37 UTC
The file ypclnt.c in the glibc-2.1 distribution has a file descriptor leak.
In particular, if ypbind is called prior to any other NIS calls (except
ypunbind), each subsequent call will leave a UDP connection open.  This is
because ypbind caches the connection, but the subsequent calls overwrite it
with a new one each time.  If you don't call ypbind first, there is no
problem.  A patch which fixes the trouble is at the end of this message.

The problem also exists in RedHat 6.0, and probably other versions.

(note that this bug was also submitted by email.  Sorry if I created a
duplicate)

The following C program demonstrates the problem.

/*****************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpcsvc/nis.h>
#include <rpcsvc/yp.h>
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/ypupd.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <bits/libc-lock.h>

main () {
  char  domain[YPMAXDOMAIN];
  unsigned int order;
  int yp_order_result;
  int i;
#define NUMCHECKS 10
  char *master;
  char *mapname = "services.byname";
  unsigned char openfile[5*NUMCHECKS];
  int fd;
  struct stat fdstat;
  char **mapkey;
  int  mapkeylen[NUMCHECKS];
  char  *mapval;
  int   mapvallen;

  mapkey = (char **)malloc(NUMCHECKS*(sizeof(char *)));

  for (fd=0; fd<5*NUMCHECKS; fd++) {
    if (fstat(fd, &fdstat) == 0) {
      openfile[fd] = 1;
    } else {
      openfile[fd] = 0;
    }
  }

  if (getdomainname(domain, sizeof(domain)) < 0) {
    fprintf(stderr,"NIS not running\n");
    exit(1);
  }


  if (  yp_bind(domain) ) {
    fprintf(stderr,"cant bind to domain %s\n");
    exit(1);
  }
  for (fd=0; fd<5*NUMCHECKS; fd++) {
    if (!openfile[fd] && fstat(fd, &fdstat) == 0) {
      printf("fd %d left open by ypbind (this is to be expected)\n",fd);
      openfile[fd] = 1;
    }
  }


  yp_order(domain, mapname, &order);
  for (fd=0; fd<5*NUMCHECKS; fd++) {
    if (!openfile[fd] && fstat(fd, &fdstat) == 0) {
      printf("fd %d left open by yp_order\n", fd);
      openfile[fd] = 1;
    }
  }


  yp_first(domain, mapname, mapkey, mapkeylen, &mapval, &mapvallen);
  for (fd=0; fd<5*NUMCHECKS; fd++) {
    if (!openfile[fd] && fstat(fd, &fdstat) == 0) {
      printf("fd %d left open by yp_first\n",fd);
      openfile[fd] = 1;
    }
  }

  for (i=1; i<NUMCHECKS; i++) {
    if (YPERR_NOMORE == yp_next(domain, mapname,
                                mapkey[i-1], mapkeylen[i-1],
                                &(mapkey[i]), &(mapkeylen[i]),
                                &mapval, &mapvallen))
      break;
  }
  for (fd=0; fd<5*NUMCHECKS; fd++) {
    if (!openfile[fd] && fstat(fd, &fdstat) == 0) {
      printf("fd %d left open by yp_next\n",fd);
      openfile[fd] = 1;
    }
  }

}
 /*****************************************************************/

Solution: A patch which remedies the problem is below.  It also fixes a
typo
 in a comment (exits was written as exists, which was worse than no
comment!)

nesconset <nis> diff -c2 ypclnt.c.orig ypclnt.c
*** glibc-2.1/nis/ypclnt.c.orig       Wed Jun 30 11:59:21 1999
--- glibc-2.1/nis/ypclnt.c    Wed Dec  8 10:12:32 1999
***************
*** 202,208 ****
          }

!       ysd->dom_socket = RPC_ANYSOCK;
!       ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG,
YPVERS,
!                                         UDPTIMEOUT, &ysd->dom_socket);
        if (ysd->dom_client == NULL)
          ysd->dom_vers = -1;
--- 202,212 ----
          }

!       /* if we don't already have a socket from a previous call, get one
*/
!       if ( ysd->dom_socket < 1 ) {
!       ysd->dom_socket = RPC_ANYSOCK;
!       ysd->dom_client = clntudp_create (&ysd->dom_server_addr,
!                                         YPPROG, YPVERS, UDPTIMEOUT,
!                                         &ysd->dom_socket);
!       }
        if (ysd->dom_client == NULL)
          ysd->dom_vers = -1;
***************
*** 211,215 ****
    while (ysd->dom_client == NULL);

!   /* If the program exists, close the socket */
    if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
      perror ("fcntl: F_SETFD");
--- 215,219 ----
    while (ysd->dom_client == NULL);

!   /* If the program exits, close the socket */
    if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
      perror ("fcntl: F_SETFD");

Comment 1 Jeff Johnson 2000-03-08 20:17:59 UTC
This problem appears to be resolved (ypc.c is the test program above, run
on ultrasparc):
bash$ cc     ypc.c   -o ypc -lnsl
bash$ ./ypc
fd 4 left open by ypbind (this is to be expected)
bash$ rpm -qf /usr/lib/libnsl.a
glibc-devel-2.1.3-15