-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Add new inherit_handles flag to CommandExt trait #115501
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
base: master
Are you sure you want to change the base?
Add new inherit_handles flag to CommandExt trait #115501
Conversation
This comment has been minimized.
This comment has been minimized.
Should this have a stabilization issue attached to it? |
First you should create an API change proposal to see if libs-api wants it. |
I recently changed the exact same interface and didn't need an ACP, is this really required here? |
|
Anyway, does windows have an equivalent to Inheritance is questionable in multi-threaded programs because different command-spawning actions may want to pass different files to the child. Also, how does one communicate to a child that a specific handle is available? On unix we've run into issues defining IO safety when passing ownership through the environment. Are there better ways to send handles to another process?
Alternatively, can we limit this to passing a set of well-defined handles? |
It is not necessary when creating a pseudoconsole to disable handle inheritance. Doing so does however allow us to skip the mutex we currently use.
Yes. You need to set
The reason the previous API change didn't require an ACP was because the change was already accepted under an older system (and I asked privately if this was still OK). A new API would need an ACP. |
Do you know why all of them set |
Okay I just tested it, indeed not necessary to set Would this still be a useful extension to the |
The most likely the simplest explanation is "because the example code does". But there are good reasons to disable handle inheritance when it's not needed: inheriting unnecessary handles is pointless and also prevents kernel objects from being cleaned up. In the worst case it may also allow the child process to mess with the handles it inherits, though it would have to find them first.
I think it would for the above reasons. |
I don't know if windows has the concept of confidential handles but preventing the child from acquiring those handles could improve security of spawning commands. |
Okay, just found the following list in the Processes can inherit or duplicate handles to the following types of objects:
|
@ChrisDenton should I message somebody about this in rust internals? |
You don't need to do anything other than be patient 🙂. ACPs are discussed in a weekly meeting (time permitting). |
Sorry, didn't know that. |
☔ The latest upstream changes (presumably #117285) made this pull request unmergeable. Please resolve the merge conflicts. |
Hi everyone, While handle inheritance can be selectively configured using the This effectively means that Rust programs running as PPL currently cannot spawn non-protected processes using the standard library, as the
|
I have an exam on Monday but after that I should be able to work on this. |
@pixmaip would the following suggestion aid your need? |
No, the suggestion you mentioned might be good for most usecases, but when running processes as PPL, passing a list of handles to inherit cannot be done. This is because PPL processes are inherently prevented by the system from leaking handles to non-protected processes, so Windows does not allow it. Note that setting |
Okay, then this should mergable independently of #123604. Please also raise your issue with the proposed solution over at rust-lang/libs-team#264 so it can be re-discussed by the lib team or maybe @ChrisDenton you can note it. |
As per rust-lang/libs-team#264 (comment), the ACP about this seems to stand firm on having only a single API that sets a specific set of handles to inherit, with the special case of an empty slice not doing anything about that and setting @michaelvanstraten do you therefore think the current PR is salvageable for this in any way? If so, do you think you would have bandwidth these days to do so? The setup code for using |
Could be a bit tricky and confusing for users, as I mentioned in rust-lang/libs-team#264 (comment).
I currently have a deadline at work, which doesn't leave me with much time. I also have exams from the beginning of February to roughly the 20th, after then it should not be an issue. I hope I can work with you on it a bit in the meantime. |
As per rust-lang/libs-team#264 (comment), this should now be possible again and pretty much as is since the part about a list of handles can be done separately. @michaelvanstraten a rebase would be needed to resolve the conflicts, please. @rustbot label -S-waiting-on-ACP |
@PaulDance, i'll get on it. |
/// **Note** that inherited handles have the same value and access rights as the original handles. For additional discussion of inheritable handles, see [Remarks][1]. | ||
/// | ||
/// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks> | ||
#[unstable(feature = "windows_process_extensions_inherit_handles", issue = "")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PaulDance would you take over the FCP to stabilize this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I'll start on this some time tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I think this needs to land first. But we should probably create a tracking issue using the template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. By "start on this", I meant first documenting myself on the involved processes that I didn't know well and then opening the tracking issue. So yes, I can push the stabilization forward when the time comes.
In the meantime, I've now opened #146407 for this, so you should be able to use it here. Some of the feature implementation steps may still need to be done, but I don't know know if they all apply to the standard library as well.
I have an idea for some basic tests for this: spawning C:\Windows\System32\cmd.exe /C "echo something"
with the flag set or not, as in one case the output should be received and in the other it should not. I'll attempt this later today and post the results further down. Any other idea is welcome, of course.
8db45eb
to
64542bf
Compare
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
@PaulDance, maybe you could add a test, I don't have a Windows machine. |
This comment has been minimized.
This comment has been minimized.
@michaelvanstraten The following works well for me and should cover the feature adequately I think: #[cfg(test)]
mod tests {
use std::os::windows::process::CommandExt;
use std::process::Command;
#[test]
fn test_windows_commandext_inherit_handles_true_stdout() {
let out = Command::new(r"C:\Windows\System32\cmd.exe")
.args(["/C", "echo Hello from StdOut!"])
.inherit_handles(true)
.output()
.unwrap();
dbg!(&out);
// Here, it is as if `inherit_handles` was not used because it defaults
// to `true`, so the following is very standard and not so surprising.
assert!(out.status.success());
assert!(out.stderr.is_empty());
assert_eq!(
str::from_utf8(&out.stdout).unwrap(),
"Hello from StdOut!\r\n"
);
}
#[test]
fn test_windows_commandext_inherit_handles_true_stderr() {
let out = Command::new(r"C:\Windows\System32\cmd.exe")
.args(["/C", "echo Hello from StdErr!>&2"])
.inherit_handles(true)
.output()
.unwrap();
dbg!(&out);
// Here, it is as if `inherit_handles` was not used because it defaults
// to `true`, so the following is very standard and not so surprising.
assert!(out.status.success());
assert!(out.stdout.is_empty());
assert_eq!(
str::from_utf8(&out.stderr).unwrap(),
"Hello from StdErr!\r\n"
);
}
#[test]
fn test_windows_commandext_inherit_handles_false_stdout() {
let out = Command::new(r"C:\Windows\System32\cmd.exe")
.args(["/C", "echo Hello from StdOut!"])
.inherit_handles(false)
.output()
.unwrap();
dbg!(&out);
// Contrary to the above tests, this now fails as the handles are
// simply not available in the process, so no write can be performed.
assert!(!out.status.success());
assert_eq!(out.status.code().unwrap(), 1);
// Both standard output and error streams therefore end up empty.
assert!(out.stdout.is_empty());
assert!(out.stderr.is_empty());
}
#[test]
fn test_windows_commandext_inherit_handles_false_stderr() {
let out = Command::new(r"C:\Windows\System32\cmd.exe")
.args(["/C", "echo Hello from StdErr!>&2"])
.inherit_handles(false)
.output()
.unwrap();
dbg!(&out);
// Contrary to the above tests, this now fails as the handles are
// simply not available in the process, so no write can be performed.
assert!(!out.status.success());
assert_eq!(out.status.code().unwrap(), 1);
// Both standard output and error streams therefore end up empty.
assert!(out.stdout.is_empty());
assert!(out.stderr.is_empty());
}
} This is using a private build that already includes the forcibly-enabled feature, so is done externally to the stdlib, hence the I guess you should be able to throw this at the CI and see what happens. Otherwise, I was simply using a local VM and it worked just fine. The bothersome part is more the cross compilation, of course. |
Looks good, I will try to integrate it later today. An alternative would be to see which handles where inherited using winapi. This could then also be used by the other new feature to pass a list of handles. |
Indeed, although it would be a bit more involved: I think it would either require a separate program compiled just for the occasion and that does this, but I don't know if this something usually acceptable, or some kind of PowerShell dark magic I don't yet know of in order to rely on already-existing programs, like what I proposed. |
I don't know if the inherited handles count towards the "open handles" but if so |
64542bf
to
5e180a5
Compare
Yes, it does, so should be tried! I guess it could for example be added to the tests I proposed as additional assertion steps of each case, but since an |
I've tried this a bit, and I'm not sure it is worth the effort:
|
So there are, "ui" tests that would allow us to do this in a bit easier without having to rely on the I will try to hack something up with what you already provided, I sadly can't cross compile or test on a VM since I'm in a camper on the road. |
@PaulDance, could you check if this works? I don't want to abuse CI for this (should I?). I would agree that the test you initially provided was more robust and would even work for the feature where you pass a list of handles to inherit. If using the |
Ah yes, this should work indeed, thanks. I currently don't have access to my work setup, so my testing would need to wait for more than a week. In the meantime however, I would say using the CI for what it is should be acceptable, as long as it's not excessive I guess. |
This patch adds a new flag to the [`CommandExt`](1) trait to set whether to inherit the handles of the calling process (2) on Windows systems. ACP: rust-lang/libs-team#264 [1]: https://doc.rust-lang.org/stable/std/os/windows/process/trait.CommandExt.html [2]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
5e180a5
to
e3ad17d
Compare
That didn't actually seam to run any tests |
I'm really not an expert in this, but maybe try using |
This PR adds a new flag to the
CommandExt
trait to set whether to inherit the handles of the calling process ([ref][1]).This is necessary when, for example, spawning a process with a
pseudoconsole
attached.r? @ChrisDenton
ACP: rust-lang/libs-team#264
[1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw