You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RetrieveProcessUserName in src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs (the Windows code path behind Get-Process -IncludeUserName) has a free-then-realloc sequence for the token info buffer that leaves the local holding a stale pointer if the reallocation throws.
The problem
Current code on master (commit 84a93015104945c16b7aa69ee67a82cb1e6499dc), lines 731-744 plus the finally at 776-781:
inttokenInfoLength=256;tokenUserInfo=Marshal.AllocHGlobal(tokenInfoLength);if(!Win32Native.GetTokenInformation(processTokenHandler,Win32Native.TOKEN_INFORMATION_CLASS.TokenUser,tokenUserInfo,tokenInfoLength,outtokenInfoLength)){error=Marshal.GetLastWin32Error();if(error==Win32Native.ERROR_INSUFFICIENT_BUFFER){Marshal.FreeHGlobal(tokenUserInfo);tokenUserInfo=Marshal.AllocHGlobal(tokenInfoLength);// if this throws, tokenUserInfo still holds the freed pointer
...
finally{if(tokenUserInfo!=IntPtr.Zero){Marshal.FreeHGlobal(tokenUserInfo);// frees the same block a second time}
...}
If the second Marshal.AllocHGlobal(tokenInfoLength) throws (out-of-memory is the realistic case), the generic catch (Exception) block in the same method swallows the exception and control reaches the finally, which frees tokenUserInfo again — although the pointer was already freed on the line above the failed allocation. The result is a double free and undefined heap behavior in the PowerShell process.
Same shape was fixed in PdhHelper.cs
#28008 fixed the identical free-realloc shape in src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs (LookupPerfNameByIndex) by zeroing the local immediately after the free:
Marshal.FreeHGlobal(localizedPathPtr);// Set the value to 'IntPtr.Zero' so a reallocation failure won't cause the stale pointer to be double-freed in the finally block below.localizedPathPtr=IntPtr.Zero;localizedPathPtr=Marshal.AllocHGlobal(strSize*sizeof(char));
The sibling site in Process.cs was not covered by that change; it still has the unfixed shape on master and in the current release branches.
Suggested fix
Mirror the #28008 pattern — reset the local right after the free, before the reallocation:
The code path is easy to reach: every elevated Get-Process -IncludeUserName call runs it, e.g. Get-Process -IncludeUserName -Id $PID | Format-List ProcessName,UserName prints ProcessName : pwsh and UserName : <DOMAIN>\<user>. The double free itself additionally requires the reallocation to throw, so it is not triggerable on demand here; the report is based on the code shape, which is the same shape Improve pointer lifecycle handling in PdhHelper #28008 addressed.
Scanning the other Marshal.FreeHGlobal sites in the tree, the free-realloc ones (PdhHelper.cs after Improve pointer lifecycle handling in PdhHelper #28008, WSManNativeAPI.cs, AclCommands.cs) reset the local or allocate only once; this site in Process.cs is the remaining one without the reset.
Summary
RetrieveProcessUserNameinsrc/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs(the Windows code path behindGet-Process -IncludeUserName) has a free-then-realloc sequence for the token info buffer that leaves the local holding a stale pointer if the reallocation throws.The problem
Current code on
master(commit84a93015104945c16b7aa69ee67a82cb1e6499dc), lines 731-744 plus thefinallyat 776-781:If the second
Marshal.AllocHGlobal(tokenInfoLength)throws (out-of-memory is the realistic case), the genericcatch (Exception)block in the same method swallows the exception and control reaches thefinally, which freestokenUserInfoagain — although the pointer was already freed on the line above the failed allocation. The result is a double free and undefined heap behavior in the PowerShell process.Same shape was fixed in PdhHelper.cs
#28008 fixed the identical free-realloc shape in
src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs(LookupPerfNameByIndex) by zeroing the local immediately after the free:The sibling site in
Process.cswas not covered by that change; it still has the unfixed shape onmasterand in the current release branches.Suggested fix
Mirror the #28008 pattern — reset the local right after the free, before the reallocation:
Notes
Get-Process -IncludeUserNamecall runs it, e.g.Get-Process -IncludeUserName -Id $PID | Format-List ProcessName,UserNameprintsProcessName : pwshandUserName : <DOMAIN>\<user>. The double free itself additionally requires the reallocation to throw, so it is not triggerable on demand here; the report is based on the code shape, which is the same shape Improve pointer lifecycle handling in PdhHelper #28008 addressed.Marshal.FreeHGlobalsites in the tree, the free-realloc ones (PdhHelper.csafter Improve pointer lifecycle handling in PdhHelper #28008,WSManNativeAPI.cs,AclCommands.cs) reset the local or allocate only once; this site inProcess.csis the remaining one without the reset.