Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak in Environment.SetEnvironmentVariable on Unix #4538

Closed
stephentoub opened this issue Oct 2, 2015 · 6 comments
Closed

Memory leak in Environment.SetEnvironmentVariable on Unix #4538

stephentoub opened this issue Oct 2, 2015 · 6 comments
Assignees
Milestone

Comments

@stephentoub
Copy link
Member

Repro:

using System;

class Program
{
    static void Main()
    {
        while (true)
        {
            Environment.SetEnvironmentVariable("testvar", "hello world"); // add it
            Environment.SetEnvironmentVariable("testvar", null);  // remove it
        }
    }
}

The problem appears to stem from here:
https://github.com/dotnet/coreclr/blob/2cf141dce5c3a22efd148e88f8a732835ee68336/src/pal/src/cruntime/misc.cpp#L687-L690

@adityamandaleeka
Copy link
Member

Looking at all of the PAL environment code and reading through the docs for the POSIX environment functions, the code we have in the PAL to deal with the environment seems dubious. It does some potentially dangerous things with the contents of environ. I came up with the following plan to solve this leak and also fix how we interact with the environment in general:

  • Read the contents of the environment once on startup and load them into some internal representation of the state of the environment.
  • Future calls to GetEnvironmentVariable and SetEnvironmentVariable affect only this internal representation of the environment, and we remove all the code we have that touches environ directly.
    • We’ll ensure that when processes are created from managed code, they’ll inherit the environment values in our internal representation.
  • The code in misc.cpp to get an environment variable (MiscGetenv) currently just returns the pointer to the string in the environment block and has a comment that says the returned value should not be modified or freed. Instead, we change it so it always makes a copy of the string and leaves it up to the caller to manage the memory. This will enable us to manage our internal representation however we see fit.
    • As part of this change, we will audit all the places where MiscGetenv is used to ensure there isn’t a hot path that would degrade due to this change.

This plan obviously means that we will not support the case where a user wants to modify the environment from managed code and expects to see those changes reflected in a native library they PInvoke to, or vice versa. However, reading and writing environment variables in a shared library is problematic anyway (without some special consideration) because none of the POSIX environment functions are thread-safe. In addition, users who really want to keep the two versions of the environment in sync will have workarounds like PInvoking set/getenv (handling the threading concerns with their own locks) and then using Environment.Get/SetEnvironmentVariable, or the other way around. On the other hand, if we use the POSIX functions internally, user code can never safely PInvoke them since they won’t have a guarantee that one of the many CLR background threads isn’t using them for something.

@janvorli
Copy link
Member

janvorli commented Feb 5, 2016

@adityamandaleeka It seems there is a problem with child processes. With your scheme, a child process launched by the managed process won't see the parent process environment changes, while it should inherit them.

@stephentoub
Copy link
Member Author

@adityamandaleeka
Copy link
Member

@janvorli I've opened a PR for this change, and I verified that Environment.SetEnvironmentVariable followed by Process.Start does work as expected and fills in the child process's native environment block properly.

@janvorli
Copy link
Member

@adityamandaleeka ok, thanks for verifying it!

@adityamandaleeka
Copy link
Member

Fixed with dotnet/coreclr#3140. Closing the issue.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 30, 2020
@msftgits msftgits added this to the 1.0.0-rc2 milestone Jan 30, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Jan 4, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants