-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Fix Start-Process -Credential -Wait to work on Windows
#19082
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
Conversation
6a89783 to
5318b3b
Compare
|
@PowerShell/wg-powershell-cmdlets discussed the issue and agreed that in the credential case, it's ok to not support the job object case with child processes |
So I believe it is safe to remove try-catch and immediately call process.WaitForExit() if Credential parameter present. |
|
@iSazonov yes, that makes sense. I'll update the code. |
5318b3b to
3e8f71a
Compare
iSazonov
left a comment
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.
LGTM with one minor comment.
src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs
Outdated
Show resolved
Hide resolved
…nt/Process.cs Co-authored-by: Ilya <[email protected]>
|
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
| // Wait for the job object to finish | ||
| jobObject.WaitOne(_waithandle); | ||
| // If we are running as a different user, we cannot use a job object, so just wait on the process | ||
| process.WaitForExit(); |
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.
Is there any reason not to always use process.WaitForExit()? Also if the user provides explicit credentials for the current user account, process.WaitForExit() will be used instead of the job object. Is this Ok?
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.
-Wait will wait not just for the new process but any processes that the new process spawns. By waiting on the job object it will wait until all of them have finished rather than just the current one.
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.
I think the intent of the job object is to also wait on any child processes, but processes in for a different user can't be added to the job object so in that case we can only WaitForExit() of the initial process. WG had discussed this and was ok with this difference.
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.
but processes in for a different user can't be added to the job object
This is not true, did you read my last comment - #19082 (comment)?
That may be true but specifying a credential does not result in the process being run in a new session, it's still the same one. A new session happens when you do something like log into RDP as another user, or run something from a service which is in session 0. Ultimately this isn't the problem here. The problem that you get today with
Now this actually works today when you are running as admin, it will wait for the process, as well as any child processes the new process spawns as expected. Where this fails is if you try and run it as a non-administrator as while the user was able to spawn the object, process permissions on the new one will not allow the caller to get the Handle needed for The problem with this fix is that while it might work in some scenarios it is technically a breaking change as Just to show that this works even for non-admins if done correctly you can run this code. # Uses Ctypes - https://www.powershellgallery.com/packages/Ctypes/0.1.0
ctypes_struct STARTUPINFOW -CharSet Unicode {
[int]$CB
[MarshalAs('LPWStr')][string]$Reserved
[MarshalAs('LPWStr')][string]$Desktop
[MarshalAs('LPWStr')][string]$Title
[int]$X
[int]$Y
[int]$XSize
[int]$YSize
[int]$XCountChars
[int]$YCountChars
[int]$FillAttribute
[int]$Flags
[short]$ShowWindow
[short]$Reserved2
[IntPtr]$Reserved3
[IntPtr]$StdInput
[IntPtr]$StdOutput
[IntPtr]$StdError
}
ctypes_struct PROCESS_INFORMATION -LayoutKind Sequential {
[IntPtr]$Process
[IntPtr]$Thread
[int]$Pid
[int]$Tid
}
[Flags()] enum ProcessCreationFlags {
NONE = 0x00000000
CREATE_SUSPENDED = 0x00000004
CREATE_NEW_CONSOLE = 0x00000010
CREATE_UNICODE_ENVIRONMENT = 0x00000400
}
$si = [STARTUPINFOW]@{
CB = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][STARTUPINFOW])
}
$pi = [PROCESS_INFORMATION]::new()
$advapi32 = New-CtypesLib Advapi32.dll
$kernel32 = New-CtypesLib Kernel32.dll
$commandLine = [System.Text.StringBuilder]::new("C:\Windows\System32\cmd.exe")
$res = $advapi32.SetLastError().CharSet('Unicode').CreateProcessWithLogonW[bool](
"vagrant",
$null,
"vagrant",
0,
$commandLine.ToString(),
$commandLine,
[int][ProcessCreationFlags]'CREATE_SUSPENDED, CREATE_NEW_CONSOLE, CREATE_UNICODE_ENVIRONMENT',
$null,
"C:\Windows",
[ref]$si,
[ref]$pi
)
if (-not $res) {
throw [System.ComponentModel.Win32Exception]$advapi32.LastError
}
$job = $kernel32.SetLastError().CreateJobObjectW[IntPtr]($null, "MyJob")
if ($job -eq [IntPtr]::Zero) {
throw [System.ComponentModel.Win32Exception]$kernel32.LastError
}
$res = $kernel32.SetLastError().AssignProcessToJobObject[bool]($job, $pi.Process)
if (-not $res) {
throw [System.ComponentModel.Win32Exception]$kernel32.LastError
}
$res = $kernel32.SetLastError().ResumeThread($pi.Thread)
if ($res -eq -1) {
throw [System.ComponentModel.Win32Exception]$kernel32.LastError
}
$kernel32.CloseHandle[void]($pi.Process)
$kernel32.CloseHandle[void]($pi.Thread)
$kernel32.CloseHandle[void]($job)I even do this in my ProcessEx module with no problems. |
|
@jborean93 Can you create a new issue about this? |
|
@PaulHigin there is one already #17033. Why would we merge a change where it changes the existing behaiour? It should be fixed properly in the first place. I'm a bit disappointed this was merged before someone actually read and addressed my last comment. Especially since PowerShell has traditionally tried to shy away from behaviour changes. It's especially concerning that a change like this was merged without:
The last point also ties into issues I had with the 7.3 release where there were a few breaking/behaviour changes but no real way for people to understand what that might be until they upgraded and things failed. |
|
@jborean93 would you be willing to submit a PR to fix this to work with a job object? |
|
Opened #19096, just need to figure out a good way to test it out. |
|
This issue has been resolved as implemented (via #19082) |
|
@doctordns What do you mean? |
|
This issue was raised to the cmdlet working group, and we felt the issue was concluded via #19082. @SteveL-MSFT agreed, so I labeled this issue as resolved-fixed. Is there a problem? |
|
You write in 19082 - it is not issue, it is PR. |
|
🎉 Handy links: |
PR Summary
Per the docs, job objects can only contain processes within the same session, so if a credential is used, we simply WaitOnExit() instead of trying to use a job object.
Validated manually since you have to wait with a different credential.
PR Context
Fix #17033
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.(which runs in a different PS Host).