April 4th, 2023

What is the maximum size of a process environment block?

A customer was getting this error from their Web server:

Server Error in ‘/’ Application.


The environment block used to start a process cannot be longer than 65535 bytes. Your environment block is 70009 bytes long. Remove some environment variables and try again.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Details: System.InvalidOperationException: The environment block used to start a process cannot be longer than 65535 bytes. Your environment block is 70009 bytes long. Remove some environment variables and try again.

Windows imposes no hard-coded limit on the size of a Unicode environment block. Here’s a test program that shows that a 256KB environment block is no problem:

#include <windows.h>
#include <stdio.h>

int main()
{
    auto n = 65535*2;
    auto p = new WCHAR[n];
    for (int i = 0; i < n; i++) {
        p[i] = L'x';
    }
    p[1] = L'=';
    p[n-1] = 0;
    p[n-2] = 0;
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    wchar_t cmdline[] = L"notepad.exe";
    printf("CreateProcess result is %d\n",
        CreateProcessW(nullptr, cmdline, nullptr, nullptr, false,
        CREATE_UNICODE_ENVIRONMENT, p, nullptr, &si, &pi));
    return 0;
}

This program creates an environment block that holds only one variable, named x whose value is 131,067 copies of the letter x. It’s not glamourous, and certainly not useful, but it does show that Windows is fine with it.

This claimed 65535 byte limit must therefore be coming from somewhere else. After some investigation, the customer found it:

namespace System.Diagnostics
{
    /* ... */

    internal static class EnvironmentBlock {
        public static byte[] ToByteArray(StringDictionary sd, bool unicode) {
            /* ... */

            if (unicode) {
                envBlock = Encoding.Unicode.GetBytes(stringBuff.ToString());
            } else {
                envBlock = Encoding.Default.GetBytes(stringBuff.ToString());

                if (envBlock.Length > UInt16.MaxValue)
                    throw new InvalidOperationException(
                        SR.GetString(SR.EnvironmentBlockTooLong,
                                     envBlock.Length));
            }

            return envBlock;
        }
    }
}

The exception is coming from the C# base class library.

Note that the check is inside the else branch, which means that somebody is passing an ANSI environment block to the CreateProcess function, and ANSI environment blocks are documented as supported up to 32,767 characters.

Chasing back to the caller, the character set is selected here:

    bool unicode = false;
#if !FEATURE_PAL                    
    if (ProcessManager.IsNt) {
        creationFlags |= NativeMethods.CREATE_UNICODE_ENVIRONMENT;                
        unicode = true;
    }
#endif // !FEATURE_PAL
    
    byte[] environmentBytes = EnvironmentBlock.ToByteArray(
            startInfo.environmentVariables, unicode);

So it appears that if you are compiled as FEATURE_PAL, the environment block is limited to 32,767 characters and must be mappable to ANSI characters.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

4 comments

Discussion is closed. Login to edit/delete existing comments.

  • Christian Klutz

    Looks like this is no longer the case with .NET (core). Where, on Windows, the environment block is always created as UNICODE, if you specify your own variables using `ProcessStartInfo.Environment` or `ProcessStartInfo.EnvironmentVariables`.

    https://github.com/dotnet/runtime/blob/e13f0dc1e0327b5d0cd0602b55ee257ef554f0a1/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs#L513-L514

    https://github.com/dotnet/runtime/blob/e13f0dc1e0327b5d0cd0602b55ee257ef554f0a1/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs#L860-L861

    Read more
  • Joshua Hudson

    Unfortunately that’s not _quite_ the right answer. The shipping copy of .NET Framework is compiled with FEATURE_PAL and there’s no way application code can change that.

    Also, most things do _NOT_ like unicode environments. The root problem is an API level bug in Windows. If the caller passes NULL for environment in Windows and has a unicode environment, the child process won’t have a unicode environment and everything blows up.

    • Daniel Roskams

      >most things do _NOT_ like unicode environments
      Really? If I just start Notepad or any other application and inspect its environment block with windbg, I see Unicode characters there. Furthermore when I write any code that involves CreateProcess(W) I more often than not pass NULL for the Environment parameter and everything works fine.
      In fact, I believe that the environment variables are *always* stored as Unicode. If you ask for the ANSI version the system...

      Read more
      • Me Gusta

        There is another possibility here, that is the CRT.