win: support environment variables > 32767 chars

The Windows documentation states these should not be possible
but several people have reported that they do in fact happen.

Try with a smallish stack-allocated buffer first and switch to
a heap-allocated buffer if the first one is too small.

Fixes: https://github.com/libuv/libuv/issues/2587
PR-URL: https://github.com/libuv/libuv/pull/2589
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
Ben Noordhuis 2020-04-28 20:11:10 +02:00
parent 75c8850c91
commit db96a61c26
2 changed files with 76 additions and 14 deletions

View File

@ -60,9 +60,6 @@
#endif
/* Maximum environment variable size, including the terminating null */
#define MAX_ENV_VAR_LENGTH 32767
/* A RtlGenRandom() by any other name... */
extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
@ -1515,7 +1512,9 @@ fail:
int uv_os_getenv(const char* name, char* buffer, size_t* size) {
wchar_t var[MAX_ENV_VAR_LENGTH];
wchar_t fastvar[512];
wchar_t* var;
DWORD varlen;
wchar_t* name_w;
DWORD bufsize;
size_t len;
@ -1529,25 +1528,52 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
if (r != 0)
return r;
SetLastError(ERROR_SUCCESS);
len = GetEnvironmentVariableW(name_w, var, MAX_ENV_VAR_LENGTH);
var = fastvar;
varlen = ARRAY_SIZE(fastvar);
for (;;) {
SetLastError(ERROR_SUCCESS);
len = GetEnvironmentVariableW(name_w, var, varlen);
if (len < varlen)
break;
/* Try repeatedly because we might have been preempted by another thread
* modifying the environment variable just as we're trying to read it.
*/
if (var != fastvar)
uv__free(var);
varlen = 1 + len;
var = uv__malloc(varlen * sizeof(*var));
if (var == NULL) {
r = UV_ENOMEM;
goto fail;
}
}
uv__free(name_w);
assert(len < MAX_ENV_VAR_LENGTH); /* len does not include the null */
name_w = NULL;
if (len == 0) {
r = GetLastError();
if (r != ERROR_SUCCESS)
return uv_translate_sys_error(r);
if (r != ERROR_SUCCESS) {
r = uv_translate_sys_error(r);
goto fail;
}
}
/* Check how much space we need */
bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL);
if (bufsize == 0) {
return uv_translate_sys_error(GetLastError());
r = uv_translate_sys_error(GetLastError());
goto fail;
} else if (bufsize > *size) {
*size = bufsize;
return UV_ENOBUFS;
r = UV_ENOBUFS;
goto fail;
}
/* Convert to UTF-8 */
@ -1560,11 +1586,23 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
NULL,
NULL);
if (bufsize == 0)
return uv_translate_sys_error(GetLastError());
if (bufsize == 0) {
r = uv_translate_sys_error(GetLastError());
goto fail;
}
*size = bufsize - 1;
return 0;
r = 0;
fail:
if (name_w != NULL)
uv__free(name_w);
if (var != fastvar)
uv__free(var);
return r;
}

View File

@ -142,5 +142,29 @@ TEST_IMPL(env_vars) {
r = uv_os_unsetenv(name2);
ASSERT(r == 0);
for (i = 1; i <= 4; i++) {
size_t n;
char* p;
n = i * 32768;
size = n + 1;
p = malloc(size);
ASSERT_NOT_NULL(p);
memset(p, 'x', n);
p[n] = '\0';
ASSERT_EQ(0, uv_os_setenv(name, p));
ASSERT_EQ(0, uv_os_getenv(name, p, &size));
ASSERT_EQ(n, size);
for (n = 0; n < size; n++)
ASSERT_EQ('x', p[n]);
ASSERT_EQ(0, uv_os_unsetenv(name));
free(p);
}
return 0;
}