[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#52222: File descriptor leak in glibc-2.1 with NIS



Package: glibc

version: 2.1

Problem description:  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.

Reported by:  Scott Sutherland <scott@math.sunysb.edu>
	
Reproduce by: 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");


Reply to: