From archie@whistle.com  Fri Jan 30 14:15:31 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 OAA06075
          for <FreeBSD-gnats-submit@freebsd.org>; Fri, 30 Jan 1998 14:15:31 -0800 (PST)
          (envelope-from archie@whistle.com)
Received: (from smap@localhost) by whistle.com (8.7.5/8.6.12) id OAA04327 for <FreeBSD-gnats-submit@freebsd.org>; Fri, 30 Jan 1998 14:03:39 -0800 (PST)
Received: from bubba.whistle.com(207.76.205.7) by whistle.com via smap (V1.3)
	id sma004319; Fri Jan 30 14:03:30 1998
Received: (from archie@localhost) by bubba.whistle.com (8.8.7/8.6.12) id OAA17264; Fri, 30 Jan 1998 14:03:30 -0800 (PST)
Message-Id: <199801302203.OAA17264@bubba.whistle.com>
Date: Fri, 30 Jan 1998 14:03:30 -0800 (PST)
From: Archie Cobbs <archie@whistle.com>
Reply-To: archie@whistle.com
To: FreeBSD-gnats-submit@freebsd.org
Subject: memory leak and other bugs in setenv(3)
X-Send-Pr-Version: 3.2

>Number:         5604
>Category:       bin
>Synopsis:       setenv(3) function has memory leak, other bugs
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jan 30 14:20:01 PST 1998
>Closed-Date:    Thu Jan 27 08:33:28 PST 2000
>Last-Modified:  Thu Jan 27 12:30:02 PST 2000
>Originator:     Archie Cobbs
>Release:        FreeBSD 2.2.5-STABLE i386
>Organization:
Whistle Communications, Inc.
>Environment:

All versions of FreeBSD.

>Description:

There is a memory leak in the setenv() function. If you overwrite
a value with a longer value, the memory allocated for the shorter
value is never freed.

Also, in certain failure cases (such as failure in the realloc()
function), the proper return value is returned (-1) but the existing
environment is destroyed.

>How-To-Repeat:

Memory leak is exhibited by this program:

  #include <stdlib.h>
  #define BSIZE 1024
  char buf[BSIZE + 1];
  int main(int ac, char *av[])
  {
    int	x;
    memset(buf, 'b', BSIZE);
    buf[BSIZE] = 0;
    for (x = 0; 1; x++)
    {
      buf[x % BSIZE] = 0;
      setenv("foo", buf, 1);
      buf[x % BSIZE] = 'b';
    }
    return(0);
  }

Also, notice what happens to "environ" in the original code when
the realloc() function call fails.

Also, the "alloced" flag is incorrectly set if the original malloc()
fails.

Overall, this function exhibits SHODDY PROGRAMMING!! :-)

>Fix:

Index: setenv.c
===================================================================
RCS file: /cvs/freebsd/src/lib/libc/stdlib/setenv.c,v
retrieving revision 1.3
diff -c -r1.3 setenv.c
*** setenv.c	1996/07/12 18:55:21	1.3
--- setenv.c	1998/01/30 21:58:33
***************
*** 54,60 ****
  {
  	extern char **environ;
  	static int alloced;			/* if allocated space before */
! 	register char *c;
  	int l_value, offset;
  
  	if (*value == '=')			/* no `=' in value */
--- 54,60 ----
  {
  	extern char **environ;
  	static int alloced;			/* if allocated space before */
! 	register char *c, *new;
  	int l_value, offset;
  
  	if (*value == '=')			/* no `=' in value */
***************
*** 73,98 ****
  
  		for (p = environ, cnt = 0; *p; ++p, ++cnt);
  		if (alloced) {			/* just increase size */
! 			environ = (char **)realloc((char *)environ,
  			    (size_t)(sizeof(char *) * (cnt + 2)));
! 			if (!environ)
  				return (-1);
! 		}
! 		else {				/* get new space */
! 			alloced = 1;		/* copy old entries into it */
! 			p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
  			if (!p)
  				return (-1);
  			bcopy(environ, p, cnt * sizeof(char *));
! 			environ = p;
  		}
! 		environ[cnt + 1] = NULL;
  		offset = cnt;
  	}
  	for (c = (char *)name; *c && *c != '='; ++c);	/* no `=' in name */
! 	if (!(environ[offset] =			/* name + `=' + value */
! 	    malloc((size_t)((int)(c - name) + l_value + 2))))
  		return (-1);
  	for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
  	for (*c++ = '='; (*c++ = *value++); );
  	return (0);
--- 73,101 ----
  
  		for (p = environ, cnt = 0; *p; ++p, ++cnt);
  		if (alloced) {			/* just increase size */
! 			p = (char **)realloc((char *)environ,
  			    (size_t)(sizeof(char *) * (cnt + 2)));
! 			if (!p)
  				return (-1);
! 		} else {	/* get new space and copy entries into it */
! 			p = (char **)malloc((size_t)
! 			    (sizeof(char *) * (cnt + 2)));
  			if (!p)
  				return (-1);
  			bcopy(environ, p, cnt * sizeof(char *));
! 			alloced = 1;
  		}
! 		environ = p;
! 		environ[cnt] = NULL;	/* indicates not previosly malloc'd */
! 		environ[cnt + 1] = NULL;		/* terminates array */
  		offset = cnt;
  	}
  	for (c = (char *)name; *c && *c != '='; ++c);	/* no `=' in name */
! 	if (!(new = malloc((size_t)((int)(c - name) + l_value + 2))))
  		return (-1);
+ 	if (environ[offset])			/* free old version, if any */
+ 		free(environ[offset]);
+ 	environ[offset] = new;
  	for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
  	for (*c++ = '='; (*c++ = *value++); );
  	return (0);

>Release-Note:
>Audit-Trail:

From: Archie Cobbs <archie@whistle.com>
To: freebsd-gnats-submit@freebsd.org
Cc:  Subject: Re: bin/5604: memory leak and other bugs in setenv(3)
Date: Fri, 30 Jan 1998 14:30:47 -0800 (PST)

 FreeBSD-gnats@FreeBSD.ORG writes:
 > Thank you very much for your problem report.
 > It has the internal identification `bin/5604'.
 > The individual assigned to look at your
 > report is: freebsd-bugs. 
 > 
 > >Category:       bin
 > >Responsible:    freebsd-bugs
 > >Synopsis:       setenv(3) function has memory leak, other bugs
 > >Arrival-Date:   Fri Jan 30 14:20:01 PST 1998
 
 Just realized the patch is incorrect in two ways:
 
  - It doesn't fix the same memory leak in unsetenv().
  - The original environ[] entries established by the loader are
    not allocated with malloc() (correct?) Therefore they should
    not be free'd.
 
 Working on a better patch...
 
 -Archie
 
 ___________________________________________________________________________
 Archie Cobbs   *   Whistle Communications, Inc.  *   http://www.whistle.com
State-Changed-From-To: open->closed 
State-Changed-By: phk 
State-Changed-When: Sun Apr 19 12:52:20 PDT 1998 
State-Changed-Why:  
waiting for better times 
State-Changed-From-To: closed->suspended 
State-Changed-By: phk 
State-Changed-When: Sun Apr 19 22:55:29 PDT 1998 
State-Changed-Why:  
Archie thinks it should be suspended, I guess he's right... 

From: Guy Helmer <ghelmer@cs.iastate.edu>
To: freebsd-gnats-submit@freebsd.org, archie@whistle.com
Cc:  
Subject: Re: bin/5604: setenv(3) function has memory leak, other bugs
Date: Thu, 03 Jun 1999 21:21:28 -0500

 Has there been progress on a fix, or should the behavior be left as-is? 
 PR bin/10341 references the same problem.
 
 Guy Helmer
 ghelmer@freebsd.org
 

From: Archie Cobbs <archie@whistle.com>
To: ghelmer@cs.iastate.edu (Guy Helmer)
Cc: freebsd-gnats-submit@freebsd.org
Subject: Re: bin/5604: setenv(3) function has memory leak, other bugs
Date: Thu, 3 Jun 1999 19:54:17 -0700 (PDT)

 Guy Helmer writes:
 > Has there been progress on a fix, or should the behavior be left as-is? 
 > PR bin/10341 references the same problem.
 
 The fact of the matter is that (according to the forces of inertia)
 "expected behavior" *implies* a memory leak. This is because if you
 do something like this:
 
   char *s;
   s = getenv("FOO");
   setenv("FOO", "new contents", 1);
 
 then the pointer "s" is supposed to still be valid. That is, the
 setenv() routine is not allowed to call free(s).
 
 This is what the FreeBSD forces of inertia have claimed anyway.
 I think there must be very few programs that rely on this, and the
 ones that do are bogus anyway, and we should just fix it. Morever,
 as I remember it was never made clear what POSIX specifies about this.
 But the FOI prevailed when I tried to fix this before.
 
 A workaround is to never overwrite a string with a shorter string.
 Overwrite with a string of the same length as the original and then
 sprintf() the shorter string onto the new one.
 
 -Archie
 
 ___________________________________________________________________________
 Archie Cobbs   *   Whistle Communications, Inc.  *   http://www.whistle.com
 

From: Peter Jeremy <jeremyp@gsmx07.alcatel.com.au>
To: adrian@FreeBSD.ORG, archie@whistle.com,
	freebsd-gnats-submit@FreeBSD.ORG
Cc:  
Subject: Re: bin/5604: setenv(3) function has memory leak, other bugs
Date: Wed, 11 Aug 1999 07:48:44 +1000

 I suspect that it isn't possible to implement setenv(3) in a manner
 that doesn't leak memory.
 
 The approach used in Solaris and OSF/1 (I'm not sure what POSIX
 mandates) is not to have a setenv(3) at all.  Instead putenv(3)
 directly manipulates the environment and requires that the string
 pointed to by its argument remain valid (ie not be an automatic
 variable).  This pushes the memory management issue onto the
 application.
 
 Until someone has a brainstorm and actually solves the bug, how
 about we document the problem:
 
 Index: getenv.3
 ===================================================================
 RCS file: /home/CVSROOT/./src/lib/libc/stdlib/getenv.3,v
 retrieving revision 1.2
 diff -u -r1.2 getenv.3
 --- getenv.3    1999/07/12 20:47:45     1.2
 +++ getenv.3    1999/08/10 21:46:15
 @@ -150,3 +150,13 @@
  .Fn putenv
  function appeared in
  .Bx 4.3 Reno .
 +.Sh BUGS
 +Successive calls to
 +.Fn setenv
 +or
 +.Fn putenv
 +assigning a differently sized
 +.Ar value
 +to the same
 +.Ar name
 +will result in a memory leak.
 
 Peter
 --
 Peter Jeremy (VK2PJ)                    peter.jeremy@alcatel.com.au
 Alcatel Australia Limited
 41 Mandible St                          Phone: +61 2 9690 5019
 ALEXANDRIA  NSW  2015                   Fax:   +61 2 9690 5982
 

From: Peter Jeremy <peter.jeremy@alcatel.com.au>
To: archie@whistle.com
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: bin/5604: setenv(3) function has memory leak, other bugs
Date: Thu, 27 Jan 2000 07:49:45 +1100

 In view of the upcoming 4.0-RELEASE freeze, together with the apparent
 impossibility to implement a setenv(3) that doesn't have memory leaks,
 could you please consider committing the following patch which at
 least documents the problem.  I would be satisfied with this as a `fix'
 for PR bin/10341 (though it doesn't address the second part of bin/5604).
 
 Index: src/lib/libc/stdlib/getenv.3
 ===================================================================
 RCS file: /home/CVSROOT/src/lib/libc/stdlib/getenv.3,v
 retrieving revision 1.3
 diff -u -r1.3 getenv.3
 --- src/lib/libc/stdlib/getenv.3	1999/08/28 00:01:31	1.3
 +++ src/lib/libc/stdlib/getenv.3	1999/10/05 06:26:19
 @@ -150,3 +150,13 @@
  .Fn putenv
  function appeared in
  .Bx 4.3 Reno .
 +.Sh BUGS
 +Successive calls to
 +.Fn setenv
 +or
 +.Fn putenv
 +assigning a differently sized
 +.Ar value
 +to the same
 +.Ar name
 +will result in a memory leak.
 
 Peter
 

From: Archie Cobbs <archie@whistle.com>
To: peter.jeremy@alcatel.com.au (Peter Jeremy)
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: bin/5604: setenv(3) function has memory leak, other bugs
Date: Wed, 26 Jan 2000 13:54:08 -0800 (PST)

 Peter Jeremy writes:
 > In view of the upcoming 4.0-RELEASE freeze, together with the apparent
 > impossibility to implement a setenv(3) that doesn't have memory leaks,
 > could you please consider committing the following patch which at
 > least documents the problem.  I would be satisfied with this as a `fix'
 > for PR bin/10341 (though it doesn't address the second part of bin/5604).
 
 Thanks, I'll do it.
 
 -Archie
 
 ___________________________________________________________________________
 Archie Cobbs   *   Whistle Communications, Inc.  *   http://www.whistle.com
 

From: Ruslan Ermilov <ru@FreeBSD.ORG>
To: Peter Jeremy <peter.jeremy@alcatel.com.au>,
	Archie Cobbs <archie@whistle.com>, Warner Losh <imp@village.org>,
	Bruce Evans <bde@zeta.org.au>
Cc:  
Subject: Re: bin/5604: setenv(3) function has memory leak, other bugs
Date: Thu, 27 Jan 2000 11:15:31 +0200

 --liOOAslEiF7prFVr
 Content-Type: text/plain; charset=us-ascii
 
 While we are on this topic, how about the following patch to setenv.c?
 
 o Back out rev 1.4 - reallocf() failure will clobber existing `environ'.
 o Do not override `environ' if realloc() fails (Obtained from: OpenBSD).
 o Set `alloced' only when memory was actually allocated.
 
 This will hopefully fix the 2nd part of this PR.
 
 <PS>Any objections if I close PR 10341 as a duplicate of PR 5604?</PS>
 
 -- 
 Ruslan Ermilov		Sysadmin and DBA of the
 ru@ucb.crimea.ua	United Commercial Bank,
 ru@FreeBSD.org		FreeBSD committer,
 +380.652.247.647	Simferopol, Ukraine
 
 http://www.FreeBSD.org	The Power To Serve
 http://www.oracle.com	Enabling The Information Age
 
 --liOOAslEiF7prFVr
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: attachment; filename=p
 
 Index: setenv.c
 ===================================================================
 RCS file: /usr/FreeBSD-CVS/src/lib/libc/stdlib/setenv.c,v
 retrieving revision 1.4
 diff -u -p -r1.4 setenv.c
 --- setenv.c	1998/09/16 04:17:45	1.4
 +++ setenv.c	2000/01/27 08:59:52
 @@ -73,16 +73,18 @@ setenv(name, value, rewrite)
  
  		for (p = environ, cnt = 0; *p; ++p, ++cnt);
  		if (alloced) {			/* just increase size */
 -			environ = (char **)reallocf((char *)environ,
 +			p = (char **)realloc((char *)environ,
  			    (size_t)(sizeof(char *) * (cnt + 2)));
 -			if (!environ)
 +			if (!p)
  				return (-1);
 +			environ = p;
  		}
  		else {				/* get new space */
 -			alloced = 1;		/* copy old entries into it */
 +						/* copy old entries into it */
  			p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
  			if (!p)
  				return (-1);
 +			alloced = 1;
  			bcopy(environ, p, cnt * sizeof(char *));
  			environ = p;
  		}
 
 --liOOAslEiF7prFVr--
 
State-Changed-From-To: suspended->closed 
State-Changed-By: ru 
State-Changed-When: Thu Jan 27 08:33:28 PST 2000 
State-Changed-Why:  
"Memory leak" is documented in getenv.3, "other bugs" are fixed in setenv.c. 

From: Peter Jeremy <peter.jeremy@alcatel.com.au>
To: Ruslan Ermilov <ru@ucb.crimea.ua>, Bruce Evans <bde@zeta.org.au>
Cc: FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: bin/5604: setenv(3) function has memory leak, other bugs
Date: Fri, 28 Jan 2000 07:20:32 +1100

 On 2000-Jan-27 20:17:39 +1100, Ruslan Ermilov <ru@FreeBSD.ORG> wrote:
 >While we are on this topic, how about the following patch to setenv.c?
 Seems good.  (And I notice you've committed it).
 
 ><PS>Any objections if I close PR 10341 as a duplicate of PR 5604?</PS>
 I previously told Archie that I'd be satisfied with documenting the
 bug as a fix.  From a different perspective, since we've fixed the
 first half of PR5604, but not touched the second half - which is what
 PR10341 covers, we might have been better off closing 5604 and
 leaving 10341 to remind us of the real problem :-/.
 
 On Thu, 27 Jan 2000 22:10:13 +1100, Bruce Evans <bde@zeta.org.au> wrote:
 >PR 10341 has the only example that I know of where the memory leak
 >matters, but we all know this now :-).
 
 I agree that example was contrived.  The problem _did_ bite me though -
 I was doing some exhaustive testing on a function that did time
 conversions in multiple timezones, and the ctime(3) functions don't
 have any way to specify a timezone other than via $TZ, so I was
 continually calling putenv(3) and wondered about the process bloat
 (10341) and poor performance (10342).
 
 Peter
 
>Unformatted:
