mingw: be *very* wary about outside environment changes

The environment is modified in most surprising circumstances, and not
all of them are under Git's control. For example, calling
curl_global_init() on Windows will ensure that the CHARSET variable is
set, adding one if necessary.

While the previous commit worked around crashes triggered by such
outside changes of the environment by relaxing the requirement that the
environment be terminated by a NULL pointer, the other assumption made
by `mingw_getenv()` and `mingw_putenv()` is that the environment is
sorted, for efficient lookup via binary search.

Let's make real sure that our environment is intact before querying or
modifying it, and reinitialize our idea of the environment if necessary.

With this commit, before working on the environment we look briefly for
indicators that the environment was modified outside of our control, and
to ensure that it is terminated with a NULL pointer and sorted again in
that case.

Note: the indicators are maybe not sufficient. For example, when a
variable is removed, it will not be noticed. It might also be a problem
if outside changes to the environment result in a modified `environ`
pointer: it is unclear whether such a modification could result in a
problem when `mingw_putenv()` needs to `realloc()` the environment
buffer.

For the moment, however, the current fix works well enough, so let's
only face the potential problems when (and if!) they occur.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2015-03-17 19:41:31 +01:00
committed by Jameson Miller
parent bfed19c720
commit ebfefac6f8

View File

@@ -1083,6 +1083,10 @@ static int do_putenv(char **env, const char *name, int size, int free_old);
static int environ_size = 0;
/* allocated size of environ array, in bytes */
static int environ_alloc = 0;
/* used as a indicator when the environment has been changed outside mingw.c */
static char **saved_environ;
static void maybe_reinitialize_environ(void);
/*
* Create environment block suitable for CreateProcess. Merges current
@@ -1092,7 +1096,10 @@ static wchar_t *make_environment_block(char **deltaenv)
{
wchar_t *wenvblk = NULL;
char **tmpenv;
int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0;
int i = 0, size, wenvsz = 0, wenvpos = 0;
maybe_reinitialize_environ();
size = environ_size;
while (deltaenv && deltaenv[i] && *deltaenv[i])
i++;
@@ -1428,6 +1435,41 @@ static int compareenv(const void *v1, const void *v2)
}
}
/*
* Functions implemented outside Git are able to modify the environment,
* too. For example, cURL's curl_global_init() function sets the CHARSET
* environment variable (at least in certain circumstances).
*
* Therefore we need to be *really* careful *not* to assume that we have
* sole control over the environment and reinitialize it when necessary.
*/
static void maybe_reinitialize_environ(void)
{
int i;
if (!saved_environ) {
warning("MinGW environment not initialized yet");
return;
}
if (environ_size <= 0)
return;
if (saved_environ != environ)
/* We have *no* idea how much space was allocated outside */
environ_alloc = 0;
else if (!environ[environ_size - 1])
return; /* still consistent */
for (i = 0; environ[i] && *environ[i]; i++)
; /* continue counting */
environ[i] = NULL;
environ_size = i + 1;
/* sort environment for O(log n) getenv / putenv */
qsort(environ, i, sizeof(char*), compareenv);
}
static int bsearchenv(char **env, const char *name, size_t size)
{
unsigned low = 0, high = size;
@@ -1481,6 +1523,7 @@ char *mingw_getenv(const char *name)
if (environ_size <= 0)
return NULL;
maybe_reinitialize_environ();
pos = bsearchenv(environ, name, environ_size - 1);
if (pos < 0)
@@ -1491,7 +1534,9 @@ char *mingw_getenv(const char *name)
int mingw_putenv(const char *namevalue)
{
maybe_reinitialize_environ();
ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc);
saved_environ = environ;
environ_size = do_putenv(environ, namevalue, environ_size, 1);
return 0;
}
@@ -2428,7 +2473,7 @@ void mingw_startup(void)
*/
environ_size = i + 1;
environ_alloc = alloc_nr(environ_size * sizeof(char*));
environ = malloc_startup(environ_alloc);
saved_environ = environ = malloc_startup(environ_alloc);
/* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
maxlen = 3 * maxlen + 1;