-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Fix for #70007, fallback to old implementation if optimized way fails #70073
Fix for #70007, fallback to old implementation if optimized way fails #70073
Conversation
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process Issue DetailsPossible fix for #70007, fallback to old implementation if optimized way to query process name fails. This might happen if executed by a user that does not have the permission to call
|
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs
Outdated
Show resolved
Hide resolved
Is there any way to write a test for this? If there is at least one Windows CI leg that runs as a non-admin user, we could have a test that finds the Process services = Process.GetProcessById(ServicesProcessId);
Assert.Equal("services", services.ProcessName); |
The API to find services.exe etc. is the API used by the old code here -- it enumerates every process on the system and gives their process id, name, and more. So then you could turn around and feed that (just the process id) back into this code, have it fail the direct query, and get the exact same data again (modulo passage of time, different stack/heap addresses). Verifying that services.exe appears, or something? |
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs
Outdated
Show resolved
Hide resolved
Where to place such a test project? |
I simplified the changes. The new code will fallback to the old implementation if |
@joperezr @ViktorHofer - do you know if we run any Windows CI legs as non-admin users? If we do, then the test could go in https://github.com/dotnet/runtime/tree/main/src/libraries/System.Diagnostics.Process/tests |
Can't directly answer that but what I know is that we have the runtime/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs Line 254 in 3d74b00
In your case you probably want an inversion: |
@schuettecarsten - are you planning on adding a test for this? |
@eerhardt, I was working on a test, but I do not think that I can make it in a reasonable time, first because of lots of other things to do at the moment, second because I did not yet try to compile the whole stuff myself for at least my local platform, just to be able to run the tests here. So, any documentation how to set this up would be helpful. |
Check out https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md for setting up your machine. And then https://github.com/dotnet/runtime/tree/main/docs/workflow/building/libraries for building the "libraries" part of the repo. |
@eerhardt - Is a test that relies on how it is executed in the build pipeline really a good idea? Of course we can analyze the process tree, look for the parents process name and check if it is services.exe. But how to figure out if it should be services.exe or can be something else? So maybe the best solution is to go back to the bug that caused this fix and simply call I rebased my branch on latest changes in main branch, no changes in code. |
This bug isn't about the parent process. It just so happens that the parent process was the way that Hosting was getting the My thinking for a test would be:
This is not an accurate assessment of the situation. Yes, there is a test in Microsoft.Extensions.Hosting that calls So maybe another way to test this is to spawn a non-elevated process from an elevated process, get the parent process, and check its name. |
I think this is the best solution. The issue has nothing to do with services, but with requesting details of the parent process from a non-evevated process. I will try to build such a test within the next few days. |
@eerhardt As a first step, I've created a simple console tool that reads the parent process name and writes it to the console. It uses the same code that IsWindowsService uses. I cannot make it compile as executable at the moment, the |
We don't need to make a new executable to do this. We have the Here's an example: runtime/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs Lines 240 to 251 in 475858d
|
Unfortunately, I am not able to provide a test for this in a reasonable time (which means in July or August). So, please take a look is this fix is valid for a merge in the current state, maybe without the half test code, as I think it is too important to fix this before net7.0 is released. |
@schuettecarsten did you try looking what's the error returned by QueryFullProcessImageName? Rather than doing a fallback, could it be possible that we are missing a permissions flag? |
Other than not having to do with parent processes, this also has nothing to do with services. To write a test I suggest this, slightly icky but not terrible: Caveats: |
@jaykrell @eerhardt can you please take a look at this snippet of a possible test for this scenario? Since @schuettecarsten won't be able to provide it, I can send a commit to this PR with the proper changes, assuming it looks good to you. [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsAndNotElevated))]
public void NonElevatedUser_QueryProcessNameOfSystemProcess()
{
const string Services = "services";
string currentProcessUser = Helpers.GetProcessUserName(Process.GetCurrentProcess());
Assert.NotNull(currentProcessUser);
Process? systemOwnedServices = null;
foreach (var p in Process.GetProcessesByName(Services))
{
// returns the username of the owner of the process or null if the username can't be queried.
// for services.exe, this will be null.
string? servicesUser = Helpers.GetProcessUserName(p);
// this isn't really verifying that services.exe is owned by SYSTEM, but we are sure it is not owned by the current user.
if (servicesUser != currentProcessUser)
{
systemOwnedServices = p;
break;
}
}
Assert.NotNull(systemOwnedServices);
Assert.Equal(Services, systemOwnedServices.ProcessName);
systemOwnedServices = Process.GetProcessById(systemOwnedServices.Id);
Assert.Equal(Services, systemOwnedServices.ProcessName);
} |
FWIW, I looked at the native exception and OpenProcess returns Unauthorized Access, regardless of PROCESS_QUERY_LIMITED_INFORMATION. So, there's really no way to avoid regressing the scenario without falling back to NtQuerySystemInformation, AFAIK. |
@jozkee - can you try running that test without this fix? Does it fail with the same exception that we are getting in #70007?
My concern is that populating a |
@eerhardt yes, the first assert uses a |
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.
This LGTM - assuming the test you added failed previously with the same exception as the bug.
It would be nice if you could manually verify the Original Report in #70007 using WindowsServiceHelpers.IsWindowsService()
.
Assert.Equal(Services, systemOwnedServices.ProcessName); | ||
} | ||
|
||
|
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.
(nit) double blank lines
It is fixed, I created the service as stated in #70007 (comment), took the new dll from |
CI issue is #45868. |
{ | ||
try | ||
{ | ||
if (Interop.OpenProcessToken(p.SafeHandle, 0x8u, out var handle)) |
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.
8?
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.
that's TOKEN_QUERY
. I didn't check what it exactly means since it was existing code, I just moved it here as I needed to reuse it.
Possible fix for #70007, fallback to old implementation if optimized way to query process name fails. This might happen if executed by a user that does not have the permission to call
QueryFullProcessImageNameW
.