Subject: Re: Memory leak in setenv(3) ?
To: None <paul@whooppee.com>
From: Simon Burge <simonb@wasabisystems.com>
List: current-users
Date: 05/30/2003 12:50:45
Paul Goyette wrote:

> I've been looking for a while to find a memory leak in one of my
> programs, and I think I've finally narrowed things down to, of all
> things, setenv(3) (and unsetenv(3), too)!
> 
> It seems that if you set an environment variable, _and_ you have the
> overwrite flag set, it will reuse the existing value's space if and
> only if the new value is  not longer than the old value.
> 
> In all other cases (new value is longer, overwrite not set, or no
> previous value exists), a new chunk of memory is malloc(3)d;  if there
> was a previous value, it is conveniently forgotten without ever being
> free(3)d!  Similarly, if you unsetenv(3) a variable, the value is just
> discarded without being free(3)d.
> 
> Granted, this probably wouldn't affect too many people, since one
> usually doesn't keep redefining the same environment variable over and
> over.  But in my case, I have a server that needs to fairly frequently
> display timestamps to users that connect, and each user has his/her
> own TZ value.  So, in order to make localtime(3) use the correct TZ, I
> need to continually update the environment variable.
> 
> Wouldn't it make sense that, if we're going to use malloc(3) to get
> space for the environment variables, we would properly use realloc(3)
> and free(3) to keep track of space no longer being used by them?

I believe it's not that simple.  See
http://sources.redhat.com/ml/cygwin/2000-07/msg00892.html for example.
See also http://mail-index.netbsd.org/tech-userlevel/2001/12/27/0002.html :-)

> Just as an aside, we _do_ use malloc(3) and realloc(3) to manage the
> list of pointers to environment variable values!   :)

I've got the following in a program I use (a multi-timezone hack for
xclock).  This is near the start of the program:

        /*
         * XXX: The longestlen, longest, setenv business is described
         * in Clock.c - look for
         *      XXX - this is not pretty
         */
        int i, longestlen;
        char *longest;
 
        ntimezones = argc - 1;
        timezones = (char **)malloc(argc * sizeof(char *));
 
        longestlen = 0;
        for (i = 0; i < ntimezones; i++) {
            timezones[i] = argv[i + 1];
            if (strlen(timezones[i]) > longestlen) {
                longest = timezones[i];
                longestlen = strlen(timezones[i]);
            }
        }
        setenv("TZ", longest, 1);

and this is in the loop where I want to call localtime with different
timezones:

#ifdef want_memory_leak
                    setenv("TZ", tz, 1);
#else
                    /* XXX - this is not pretty!  but setenv() leaks memory :-( */
                    char *envptr;
                    envptr = getenv("TZ");
                    strcpy(envptr, tz);
#endif
                    tm = *localtime(&time_value);

Not nice, but get's the job done.  Having something like a
localtime_tz(3) with an extra arg for the timezone would be nice...

Simon.
--
Simon Burge                            <simonb@wasabisystems.com>
NetBSD Support and Service:         http://www.wasabisystems.com/