From: Martino Fontana tinozzo123@gmail.com
Windows has a limit of 32767 characters in the environment block. https://superuser.com/questions/1070272/why-does-windows-have-a-limit-on-env...
Wine doesn't attempt to respect that at all. Do some programs actually depend on this? Unfortunately yes.
With this patch, during initialization, if the block size would exceed (or be close to) the limit, the biggest environment variables will be excluded.
This is useful when a user has very long environment variables in their system for reasons unrelated to Wine.
Do note that there's still nothing done to make sure that the limit isn't exceeded after initialization.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56941 Signed-off-by: Martino Fontana tinozzo123@gmail.com --- dlls/kernelbase/process.c | 2 -- dlls/ntdll/unix/env.c | 47 ++++++++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 2d08481cd35..835cdf654fc 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1610,8 +1610,6 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetEnvironmentVariableA( LPCSTR name, LPSTR value NTSTATUS status; DWORD len, ret;
- /* limit the size to sane values */ - size = min( size, 32767 ); if (!(valueW = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return 0;
RtlCreateUnicodeStringFromAsciiz( &us_name, name ); diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c index 36949b905fb..4c29921ddbc 100644 --- a/dlls/ntdll/unix/env.c +++ b/dlls/ntdll/unix/env.c @@ -919,6 +919,15 @@ static const char overrides_help_message[] = "Example:\n" " WINEDLLOVERRIDES="comdlg32=n,b;shell32,shlwapi=b"\n";
+struct str_len { + char *str; + size_t len; +}; + +static int cmp_str_len(const void *a, const void *b) { + return ((struct str_len*) a)->len - ((struct str_len*) b)->len; +} + /************************************************************************* * get_initial_environment * @@ -926,20 +935,46 @@ static const char overrides_help_message[] = */ static WCHAR *get_initial_environment( SIZE_T *pos, SIZE_T *size ) { - char **e; WCHAR *env, *ptr, *end; + SIZE_T i, env_count = 0; + struct str_len *env_vars; + /* Windows has a limit of 32767 characters for the environment block, and + * some programs malfunction if this is exceeded. As a workaround, don't add + * the biggest environment variables. Leave some space for variables that + * the program itself might add. */ + /* FIXME: Prevent exceeding the limit after initialization. */ + const SIZE_T MAX_ENV_SIZE = 30000; + + while (main_envp[env_count]) env_count++; + + env_vars = calloc(env_count, sizeof(struct str_len));
/* estimate needed size */ *size = 1; - for (e = main_envp; *e; e++) *size += strlen(*e) + 1; + for (i = 0; i < env_count; i++) { + env_vars[i].str = main_envp[i]; + env_vars[i].len = strlen(main_envp[i]) + 1; + *size += env_vars[i].len; + } + if (*size > MAX_ENV_SIZE){ + /* sort environment variables by length in ascending order, to exclude + * only the biggest ones */ + qsort(env_vars, env_count, sizeof(struct str_len), cmp_str_len); + *size = MAX_ENV_SIZE; + }
env = malloc( *size * sizeof(WCHAR) ); ptr = env; end = env + *size - 1; - for (e = main_envp; *e && ptr < end; e++) - { - char *str = *e; + for (i = 0; i < env_count; i++) { + char *str = env_vars[i].str;
+ if (ptr - env + env_vars[i].len >= MAX_ENV_SIZE) + { + WARN("Environment variables block exceeds or is close to the 32767 " + "characters limit. Removing the biggest variables.\n"); + break; + } /* skip Unix special variables and use the Wine variants instead */ if (!strncmp( str, "WINE", 4 )) { @@ -947,6 +982,7 @@ static WCHAR *get_initial_environment( SIZE_T *pos, SIZE_T *size ) else if (!strcmp( str, "WINEDLLOVERRIDES=help" )) { MESSAGE( overrides_help_message ); + free(env_vars); exit(0); } } @@ -956,6 +992,7 @@ static WCHAR *get_initial_environment( SIZE_T *pos, SIZE_T *size ) ptr += ntdll_umbstowcs( str, strlen(str) + 1, ptr, end - ptr ); } *pos = ptr - env; + free(env_vars); return env; }