Environment clearing broken in 7.0

Sean C. Farley scf at FreeBSD.org
Tue Jun 10 17:17:27 UTC 2008


On Tue, 10 Jun 2008, Timo Sirainen wrote:

> On Mon, 2008-06-09 at 22:27 -0500, Sean C. Farley wrote:
>> On Mon, 9 Jun 2008, Timo Sirainen wrote:
>>
>>> I think clearing environment using:
>>>
>>> environ[0] = NULL;
>>>
>>> has been kind of a semi-standard for a while now. At least Dovecot
>>> and Postfix clears their environment this way. But this no longer
>>> works in FreeBSD 7.0 (putenv(), environ[0]=NULL, putenv() ->
>>> everything is visible again). Was this change intended, or will this
>>> be fixed?
>>
>> It is more or less intended.  When a program sets an environment
>> variable, the environment is copied for faster/leaner usage.
>> Changing individual values within environ is not checked else every
>> pointer would need to be checked for consistency.  What I did was to
>> write the code to detect if environ is replaced (NULL or new array of
>> variables).
>
> OK, so perhaps Sendmail's way of clearing environment would be the
> best solution:
>
> static char *emptyenv[1] = { NULL };
> environ = emptyenv;

That works too.

>> I suggest reading the two paragraphs from Open Group's getenv()[1]
>> documentation starting at "Conforming applications are required not
>> to modify environ directly, ..." for the rationale in the new design.
>> Obviously, applications are not required to conform, but the
>> documentation talks about what an OS may be doing under the covers to
>> environ.
>
> How about implementing clearenv()? I'm using it now if it's available.

It is a thought.  It is not part of SUSv3, but there are many API calls
in our libc that are not part of that spec.

Interestingly, clearenv() on Linux ends up setting environ=NULL.  Also,
from the Linux man page:

     The DG/UX and Tru64 manpages write: If environ has been modified by
     anything other than the putenv(), getenv(), or clearenv() functions,
     then clearenv() will return an error and the process environment
     will remain unchanged.

Hopefully, no libraries on these systems are manipulating environ else
clearenv() will not work.

>> Out of curiosity, do Dovecot and Postfix check that environ is not
>> NULL before setting environ[0]?  environ may be set to NULL at the
>> start but not by FreeBSD's /usr/bin/env -i.
>
> Yes, both check if it's NULL. (I think I originally copied my code's
> logic from Postfix.)

Good.  I asked because I could see myself making that mistake awhile
back before I truly understood how environ interacted with applications
from start (exec()) to finish.

>>> Looks like I could work around this by using:
>>>
>>> environ = NULL;
>>
>> That will work on the *BSD's, OpenSolaris and Linux.
>
> But not on OS X. It crashes there.

Grrrr.

>> Also, this will work:
>> environ = calloc(1, sizeof(*environ));
>
> Is this any better than using a static emptyenv[1]?

Not exactly but see below about Haiku as an example.

> BTW. I wonder if this change breaks any applications where not
> clearing environment could result in a security hole. As far as I know
> FreeBSD 7.0 is the only modern OS where environ[0]=NULL doesn't work.

OpenSolaris also does not detect environ[0]=NULL.  Haiku[1], like MacOS,
does not handle environ=NULL.  *sigh*  To support the most OS's I
recommend the environ replacement such as in the static environ above.

IMHO, mixing direct usage of the internals of environ and calling *env()
is not safe.  I really wish that environ could not be touched directly
by any application.

To be safe with all implementations of environ, I recommend using a
method that replaces environ with a NULL terminated array such as you
have with emptyenv.  This will work on all platforms.  Oops!  Actually,
it may break on Haiku since it will realloc() environ if it has copied
environ previously with a call to putenv(), setenv() or unsetenv().

No guarantees, but I will do some research about detecting a NULL at
environ[0] as another means of clearing the environment and/or writing
an implementation of clearenv().  Of course, you will still have
problems on OpenSolaris.  What are you planning to do there, or does it
support cleanenv()?

Does anyone know why clearenv() was rejected?  There is hardly a peep on
the OpenGroup web site.

To consolidate the notes:

environ = NULL does not work on:
1. MacOS/Darwin
2. Haiku

environ[0] = NULL does not work on:
1. FreeBSD 7+
2. OpenSolaris

static char *emptyenv[1] = { NULL };
environ = emptyenv; does not work on:
1. Haiku
2. MacOS?

environ = calloc(1, sizeof(*environ)); should work on all assuming NULL
was not returned.

cleanenv() works on all systems that support it with caveats concerning:
1. DG/UX and Tru64

Sean
   1. http://dev.haiku-os.org/browser/haiku/trunk/src/system/libroot/posix/stdlib/env.c
-- 
scf at FreeBSD.org


More information about the freebsd-stable mailing list