-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Make Process.Start have a option to change handle inheritance #13943
Comments
I agree we should add this API. IMO we should also make TcpListener have option to change HandleInheritability and change the default behavior to not inherit handles if possible (at least when targeting newer version of the framework). In most cases we should not change InheritHandles on Process and should rather do it whenever we create handles but we should still have an option to disable this. |
I think adding options to TcpListener/TcpClient/Socket would be a good idea. Are there other places where handle inheritance can be a problem? I was not able to create a problem like the one mentioned above by exclusivly opening a file |
This seems quite reasonable. As @krwq we should probably do a pass and look into similar classes that perform handle inheritability. This helps in understanding what API we could use. |
@pdelvo, We were able to repro this with FileStream although that one creates non-inheritable handles by default so had to override that. I believe that every library should have non-inheritable handles by default but that might be hard to change at this point because of compat reasons |
One open question I have is what this would mean when we try to support this feature cross platform. What would it mean to say that handles are not inherited in Unix? Does "handles" here mean open file descriptors? If they are not to be inherited, does the framework have to find and close them in the child process? How does it interact with file descriptors marked FD_CLOEXEC? |
I expect there are going to be a fair number of features in System.Diagnostics.Process that result in PlatformNotSupportedException; this might be one of them, if e.g. explicitly closing all fds above 2 doesn't work out or is prohibitive for some reason. |
We've reviewed this proposal and don't believe it's ready yet. Please take a look at the notes. |
@Priya91 yeah I could do something, but likely not until after VS 2017 ships (it is an all consuming effort). If @pdelvo wants to start on something, I'd be happy to collaborate as well. What are the requirements for CoreFx API changes? Does the API need x-plat support, can there be Windows specific elements? Etc. As a side note, I'd prefer to see a method added to |
We have the api addition process documented here. It also elaborates on the design principles. Yeah we do need x-plat support, as .NET Core supports Unix platforms as well.
Are you asking in terms of exposing an API only on Windows? We can't do that, although we have some windows specific APIs in .NET Core and throw PNSE on other platforms. For now the focus should be more on the API design as you suggested in using a method over property etc, which will take shape once we understand the scenarios and requirements on all platforms. |
@Priya91 👍 and thanks. |
@pdelvo do you want to update per the feedback and retry review? that would be welcome. |
Next steps: We need to decide how to introduce Windows-only APIs. Ideally BCL is mostly platform agnostic. It is something we want in BCL. |
@agocke had some interest in this issue with https://github.com/dotnet/corefx/issues/26711, anybody know what is the latest? |
In node.js they use the concept from Win32 API See process creation flags: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx Then in Unix, they also use detached process from the options and then call Maybe instead of calling it cc @wfurt |
@kasper3 that's fine for NodeJs which is launch processes to behave like threads. Often, developers want to share the console's standard in/out/err pipes with the child. Additionally, neither @karelz why not just have the |
According to this thread (brettwooldridge/NuProcess#13 (comment)), it sounds like Java is able to solve this issue in it's Process implementation:
Probably .NET could do something similar? |
@madelson on Linux? sure. On Windows? not so much. |
@whoisj The OP calls out that today we call CreateProcess with bInheritHandles = true (hard coded). So on Windows I assumed the solution would be to just pass false for this parameter. From the discussion I thought the main issue was a concern that this behavior couldn't easily be replicated across OS's (e. g. @Priya91's comment about this API being Windows only). |
@madelson passing Instead, I would think (for Windows) we'd want to focus on |
I know exactly why this bug exists. The Process.Start() code was written originally to run on Windows 9x where you literally couldn't fix this. The API for actually fixing this on Windows was added on Windows Vista, but nobody ever revisited this code. And when the port to Linux was done it was done directly, emulating the bug, probably without thinking about it. Anybody using blind inherit handles from any thread other than the main thread is bugged and doesn't know it yet. |
@jhudsoncedaron do you have specific changes in mind? |
Yup! How to fix (Windows): https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 To fix on Unix, the child process needs to enumerate over /proc/self/fd and close any integers > 2 except for the handle being used to enumerate the directory itself. |
This is not a "bug", which suggests you could just change the implementation... doing that would break an absolute ton of code. If we want to add an option to the API (and work through any associated issues), that's fine, but we must not change the existing behavior of the existing API. And we absolutely thought about the behavior when implementing this for Unix. |
@stethentoub: If you create two child process from two separate threads without a lock on the unix platform, your code can hang. I'm not certain but I think the build server code in dotnet is affected. It's difficult to come up with a case on the Windows platform where someone could be depending on random handles being inherited because only socket handles are inherited. |
e.g. anonymous pipes
Code can hang for lots of reasons. My point is, the implementation of the API is not buggy; it's doing exactly what it's defined to do. You may not like the definition, but that's different than saying there's a bug in the implementation. |
Going through stale issues -- this is well understood and can be up for grabs, right? Anyone interested in offering a PR? |
@danmoseley I think this issue is important for quite a lot of users (see 👍🏼-Reactions of the top post). |
@deeprobin fyi, because .NET opens handles with CLOEXEC, this is less of a problem now on Linux than when the issue was first created. Future milestone means Microsoft doesn't have it planned for .NET 7. Community can still make the contribution. |
Is it even feasible @tmds? It feels like we would need to change all existing open handles, right? (e.g. undo CLOEXEC) I'm wondering if we should focus on API to pass specific handle to child process. (and close this) |
@deeprobin Feel free to grab this issue if you are interested.
It is not reasonably possible to implement this proposal on non-Windows. It is why the property is marked as Windows-specific. It is addressing a specific real problem on Windows.
It is very hard to design a uniform API set around this. Windows and Unix treat handles very differently during create process. |
@jkotas : The proposed API from ages ago that you give a list of handles to inherit can be implemented on both. You can undo CLOEXEC in the fork child with fcntl calls on Unix, and you can use the CreateProcess thing that gives the list of handles to inherit on Windows. |
I think the main use-case for this API is to prevent handles from leaking into the child by setting So in case of And in case of
On Unix, the child process expects the caller to pass files as specific fds (by using dup). On Windows, the handle value stays the same. And it is communicated to the child process for example via an argument as shown in the documentation of See #13943 (comment) and next comments about a potential |
I can take a look at the Issue. I think the Windows implementation will not be that difficult. @tmds is the whole thing possible on Unix to the extent of this API? |
@deeprobin : I can implement on Linux even on very old kernels. Need somebody else for Mac OSX though. It is not allowed to call |
I run into a small problem in the Windows implementation.
|
Also, I'm not sure how to proceed if the property UseShellExecute = true as well as InheritHandles = true, which technically is not possible through the Windows API. |
@deeprobin : I'd expect a throw if both UseShellExecute and InheritHandles are set. CreateProcessWithLogonW is bugged in Windows. What I end up having to do is give it an executable that I control and fix stuff up on the other side. |
that is an incredibly bold statement, and I am not sure it is correct. in fact, I highly doubt that it is correct. an API as fundamental as You might notice that I was asked to propose an API for this, but that never happened because I left MSFT shortly thereafter for my current role. Even so, the best method to achieve what people are looking for here is to use |
@whoisj : The time has long passed to ship a service that provides new API endpoints for login management. I have been faced with too many arbitrary limitations because no API at all does what is necessary and everybody hates it when you reach for NtCreateToken because there is no other. One scenario: My code has admin rights and needs to start a process as a user that it has username and password for, but the user only has privileges to log in as a service. I have STDIN, STDOUT, and STDERR redirects that need to be placed. I ought to be able to do this, but I can't because of arbitrary restrictions. |
@jhudsoncedaron if you're on Windows there are advapi32!SaferCreateLevel and advapi32!CreateProcessAsUserW which do most of what you want. You'll likely have to use kernel32!CreateNamedPipeW to create proper stdin, stdout, and stderr pipes to make it work ; but it CAN work - I've done it. That how Visuals Studio executes |
@whoisj : CreateProcessAsUserW requires SE_ASSIGNPRIMARYTOKEN_NAME; which admins don't have. I tried that already. I was able to get some parts of it working by spawning a service, but ultimately process ends up in the wrong session if I do that. |
when user run Visual Studio elevated (many do), VS runs git not elevated, and it uses these are just regular users using the "Run as administrator" option - as you know, Windows does have needless "root" accounts - so they're just running as an elevated version of their normal user account. This works just fine for Visual Studio. There's no magic "secret sauce" or "hidden API" involved, it's all public stuff. |
@whoisj: Oh. You missed "user only has privileges to log in as a service" out of the scenario. |
ah my mistake. not sure how to resolve that issue as I've never tried. sorry. |
To beat a dead horse, this was the root cause of a tricky bug in a CLI framework we use internally that handles automatic telemetry collection. To avoid blocking your shell while waiting to send that telemetry, the framework spawns an instance of the CLI with event data so it can go send it in the background and not wait (fire and forget). This works great at a shell/terminal. Shaves off almost 500ms of runtime. It also works great when calling these tools from other languages where handles are not being inherited automatically. But if another Dotnet process, shells out to one of these apps, it ends up getting blocked on both the main CLI process and any children telemetry processes. We've mitigated this by moving to |
Since this API is approved I'd suggest if anyone is interested to contribute. Even throwing PNSE on platforms you currently don't plan to support would improve the state. Most people don't need this and it's only useful when you actually encounter bugs like mentioned above. Make sure to look at the API notes and let us know if you plan to pick this up so that no one duplicates work. For now I can set milestone to 9.0 so this issue is considered for 9.0 (given number of upvotes, weird issues affected people can see, how long this has been open and how relatively cheap it is to add support at least on Windows - haven't investigated Linux/macOS) - note this doesn't guarantee we will actually do anything in 9.0 but we can at least consider this - we'll look at the area holistically and pick which issues are most important. |
Currently if you call Process.Start internally CreateProcess is called with bInheritHandles = true (hard coded). It would be great to make it possible to change this behavior, e.g. by adding a Property to ProcessStartInfo.
Currently there is no way I know of to change this other then reimplementing System.Diagnostics.Process.
Example
If you run this application twice without exiting the first notepad instance the second instance will not be able to open the tcp port, because notepad is still running. This can be a problem for server applications that are starting child processes themself and crash, or are killed by the user before the socket can be closed.
Design proposal
The easiest way to make this possible is to add a new Property to ProcessStartInfo and use this in the Call to CreateProcess
Questions
The text was updated successfully, but these errors were encountered: