From archie@whistle.com  Sun Sep 13 20:30:58 1998
Received: from whistle.com (s205m131.whistle.com [207.76.205.131])
          by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id UAA16001
          for <FreeBSD-gnats-submit@freebsd.org>; Sun, 13 Sep 1998 20:30:58 -0700 (PDT)
          (envelope-from archie@whistle.com)
Received: (from smap@localhost) by whistle.com (8.7.5/8.6.12) id UAA16722 for <FreeBSD-gnats-submit@freebsd.org>; Sun, 13 Sep 1998 20:30:43 -0700 (PDT)
Received: from bubba.whistle.com(207.76.205.7) by whistle.com via smap (V1.3)
	id sma016720; Sun Sep 13 20:30:41 1998
Received: (from archie@localhost) by bubba.whistle.com (8.8.7/8.6.12) id UAA15858; Sun, 13 Sep 1998 20:30:41 -0700 (PDT)
Message-Id: <199809140330.UAA15858@bubba.whistle.com>
Date: Sun, 13 Sep 1998 20:30:41 -0700 (PDT)
From: Archie Cobbs <archie@whistle.com>
Reply-To: archie@whistle.com
To: FreeBSD-gnats-submit@freebsd.org
Subject: scandir(3) does not clean up after itself if it fails
X-Send-Pr-Version: 3.2

>Number:         7923
>Category:       bin
>Synopsis:       scandir(3) does not clean up after itself if it fails
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Sep 13 20:40:01 PDT 1998
>Closed-Date:    Tue Oct 6 17:54:39 PDT 1998
>Last-Modified:  Tue Oct  6 18:31:45 PDT 1998
>Originator:     Archie Cobbs
>Release:        FreeBSD 2.2.6-RELEASE i386
>Organization:
Whistle Communications, Inc.
>Environment:

	FreeBSD 3.0-current circa July 13, 1998 20:00 GMT

>Description:

	The scandir() function returns -1 if it fails.
	In many cases when this happens, it does not free
	the memory that it allocated, resulting in a memory
	leak, or close the directory opened with opendir().
	BAD DOG, BAD!

>How-To-Repeat:


>Fix:
	
Index: scandir.c
===================================================================
RCS file: /cvs/freebsd/src/lib/libc/gen/scandir.c,v
retrieving revision 1.3
diff -c -u -r1.3 scandir.c
--- scandir.c	1995/05/30 05:40:23	1.3
+++ scandir.c	1998/09/14 03:29:09
@@ -66,8 +66,8 @@
 	int (*select) __P((struct dirent *));
 	int (*dcomp) __P((const void *, const void *));
 {
-	register struct dirent *d, *p, **names;
-	register size_t nitems;
+	register struct dirent *d, *p, **names = NULL;
+	register size_t nitems = 0;
 	struct stat stb;
 	long arraysz;
 	DIR *dirp;
@@ -75,7 +75,7 @@
 	if ((dirp = opendir(dirname)) == NULL)
 		return(-1);
 	if (fstat(dirp->dd_fd, &stb) < 0)
-		return(-1);
+		goto fail;
 
 	/*
 	 * estimate the array size by taking the size of the directory file
@@ -84,9 +84,8 @@
 	arraysz = (stb.st_size / 24);
 	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
 	if (names == NULL)
-		return(-1);
+		goto fail;
 
-	nitems = 0;
 	while ((d = readdir(dirp)) != NULL) {
 		if (select != NULL && !(*select)(d))
 			continue;	/* just selected names */
@@ -95,7 +94,7 @@
 		 */
 		p = (struct dirent *)malloc(DIRSIZ(d));
 		if (p == NULL)
-			return(-1);
+			goto fail;
 		p->d_fileno = d->d_fileno;
 		p->d_type = d->d_type;
 		p->d_reclen = d->d_reclen;
@@ -105,22 +104,36 @@
 		 * Check to make sure the array has space left and
 		 * realloc the maximum size.
 		 */
-		if (++nitems >= arraysz) {
-			if (fstat(dirp->dd_fd, &stb) < 0)
-				return(-1);	/* just might have grown */
+		if (nitems + 1 >= arraysz) {
+			struct dirent **names2;
+
+			if (fstat(dirp->dd_fd, &stb) < 0) {
+				free(p);
+				goto fail;	/* just might have grown */
+			}
 			arraysz = stb.st_size / 12;
-			names = (struct dirent **)realloc((char *)names,
+			names2 = (struct dirent **)realloc((char *)names,
 				arraysz * sizeof(struct dirent *));
-			if (names == NULL)
-				return(-1);
+			if (names2 == NULL) {
+				free(p);
+				goto fail;
+			}
+			names = names2;
 		}
-		names[nitems-1] = p;
+		names[nitems++] = p;
 	}
 	closedir(dirp);
 	if (nitems && dcomp != NULL)
 		qsort(names, nitems, sizeof(struct dirent *), dcomp);
 	*namelist = names;
 	return(nitems);
+
+fail:
+	while (nitems > 0)
+		free(names[--nitems]);
+	free(names);
+	closedir(dirp);
+	return -1;
 }
 
 /*
>Release-Note:
>Audit-Trail:

From: Archie Cobbs <archie@whistle.com>
To: freebsd-gnats-submit@freebsd.org
Cc:  Subject: Re: bin/7923: scandir(3) does not clean up after itself if it fails
Date: Mon, 14 Sep 1998 14:02:39 -0700 (PDT)

 Wait! There's also another bug in that sometime (esp when scanning /proc)
 it fails to realloc() the correct amount. Please use this patch instead.
 
 Thanks,
 -Archie
 
 ___________________________________________________________________________
 Archie Cobbs   *   Whistle Communications, Inc.  *   http://www.whistle.com
 
 Index: scandir.c
 ===================================================================
 RCS file: /cvs/freebsd/src/lib/libc/gen/scandir.c,v
 retrieving revision 1.3
 diff -c -u -r1.3 scandir.c
 --- scandir.c	1995/05/30 05:40:23	1.3
 +++ scandir.c	1998/09/14 21:02:08
 @@ -66,8 +66,8 @@
  	int (*select) __P((struct dirent *));
  	int (*dcomp) __P((const void *, const void *));
  {
 -	register struct dirent *d, *p, **names;
 -	register size_t nitems;
 +	register struct dirent *d, *p, **names = NULL;
 +	register size_t nitems = 0;
  	struct stat stb;
  	long arraysz;
  	DIR *dirp;
 @@ -75,7 +75,7 @@
  	if ((dirp = opendir(dirname)) == NULL)
  		return(-1);
  	if (fstat(dirp->dd_fd, &stb) < 0)
 -		return(-1);
 +		goto fail;
  
  	/*
  	 * estimate the array size by taking the size of the directory file
 @@ -84,9 +84,8 @@
  	arraysz = (stb.st_size / 24);
  	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
  	if (names == NULL)
 -		return(-1);
 +		goto fail;
  
 -	nitems = 0;
  	while ((d = readdir(dirp)) != NULL) {
  		if (select != NULL && !(*select)(d))
  			continue;	/* just selected names */
 @@ -95,7 +94,7 @@
  		 */
  		p = (struct dirent *)malloc(DIRSIZ(d));
  		if (p == NULL)
 -			return(-1);
 +			goto fail;
  		p->d_fileno = d->d_fileno;
  		p->d_type = d->d_type;
  		p->d_reclen = d->d_reclen;
 @@ -105,22 +104,33 @@
  		 * Check to make sure the array has space left and
  		 * realloc the maximum size.
  		 */
 -		if (++nitems >= arraysz) {
 -			if (fstat(dirp->dd_fd, &stb) < 0)
 -				return(-1);	/* just might have grown */
 -			arraysz = stb.st_size / 12;
 -			names = (struct dirent **)realloc((char *)names,
 -				arraysz * sizeof(struct dirent *));
 -			if (names == NULL)
 -				return(-1);
 +		if (nitems >= arraysz) {
 +			const int inc = 10;	/* increase by this much */
 +			struct dirent **names2;
 +
 +			names2 = (struct dirent **)realloc((char *)names,
 +				(arraysz + inc) * sizeof(struct dirent *));
 +			if (names2 == NULL) {
 +				free(p);
 +				goto fail;
 +			}
 +			names = names2;
 +			arraysz += inc;
  		}
 -		names[nitems-1] = p;
 +		names[nitems++] = p;
  	}
  	closedir(dirp);
  	if (nitems && dcomp != NULL)
  		qsort(names, nitems, sizeof(struct dirent *), dcomp);
  	*namelist = names;
  	return(nitems);
 +
 +fail:
 +	while (nitems > 0)
 +		free(names[--nitems]);
 +	free(names);
 +	closedir(dirp);
 +	return -1;
  }
  
  /*
State-Changed-From-To: open->closed 
State-Changed-By: julian 
State-Changed-When: Tue Oct 6 17:54:39 PDT 1998 
State-Changed-Why:  
Patch applied 
>Unformatted:
