Skip to content
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

gh-116195: Implement win32_getppid_fast #116205

Merged
merged 11 commits into from
Mar 14, 2024
93 changes: 91 additions & 2 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -9115,16 +9115,105 @@ os_setpgrp_impl(PyObject *module)
#ifdef HAVE_GETPPID

#ifdef MS_WINDOWS
#include <processsnapshot.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this header file is still required because we are using PssCaptureSnapshot bellow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it is - missed that. Is there a way I can add commits to the PR without making a new one? On other repos just committing to my own branch worked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just add new commit to your branch, and this PR will be updated. Close a PR and create new one will make the review work harder.

#include <winternl.h>
#include <ProcessSnapshot.h>

// The structure definition in winternl.h may be incomplete.
// This structure is the full version from the MSDN documentation.
typedef struct _PROCESS_BASIC_INFORMATION_FULL {
NTSTATUS ExitStatus;
PVOID PebBaseAddress;
ULONG_PTR AffinityMask;
LONG BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION_FULL;

typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) (
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL);

// This function returns the process ID of the parent process.
// Returns 0 on failure.
static ULONG
win32_getppid_fast(void)
{
NTSTATUS status;
HMODULE ntdll;
PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess;
PROCESS_BASIC_INFORMATION_FULL basic_information;
static ULONG cached_ppid = 0;

if (cached_ppid) {
// No need to query the kernel again.
return cached_ppid;
}

ntdll = GetModuleHandleW(L"ntdll.dll");
if (!ntdll) {
return 0;
}

pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(ntdll, "NtQueryInformationProcess");
if (!pNtQueryInformationProcess) {
return 0;
}

status = pNtQueryInformationProcess(
GetCurrentProcess(),
ProcessBasicInformation,
&basic_information,
sizeof(basic_information),
NULL);
vxiiduu marked this conversation as resolved.
Show resolved Hide resolved

if (!NT_SUCCESS(status)) {
return 0;
}

//
// Perform sanity check on the parent process ID we received from NtQueryInformationProcess.
// The check covers values which exceed the 32-bit range (if running on x64) as well as
// zero and (ULONG) -1.
//
vxiiduu marked this conversation as resolved.
Show resolved Hide resolved

if (basic_information.InheritedFromUniqueProcessId == 0 ||
basic_information.InheritedFromUniqueProcessId >= ULONG_MAX)
{
return 0;
}

//
// Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId
// structure member contains a ULONG_PTR which represents the process ID of our parent
// process. This process ID will be correctly returned even if the parent process has
// exited or been terminated.
//
vxiiduu marked this conversation as resolved.
Show resolved Hide resolved

cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId;
return cached_ppid;
}

static PyObject*
win32_getppid(void)
{
DWORD error;
PyObject* result = NULL;
HANDLE process = GetCurrentProcess();

HPSS snapshot = NULL;
ULONG pid;

pid = win32_getppid_fast();
if (pid != 0) {
return PyLong_FromUnsignedLong(pid);
}

//
// If failure occurs in win32_getppid_fast(), fall back to using the PSS API.
//
vxiiduu marked this conversation as resolved.
Show resolved Hide resolved

error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot);
if (error != ERROR_SUCCESS) {
return PyErr_SetFromWindowsErr(error);
Expand Down