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: