Skip to content

Commit dcbd5a1

Browse files
committed
fixup! trace2: collect Windows-specific process information
Guard against infinite loop while computing the parent process hierarchy. CreateToolhelp32Snapshot() is used to get a list of all processes on the system. Each process entry contains the process PID and PPID (alive at the time of the snapshot). We compute the set of ancestors of the current process by repeated searches on this list. Testing revealed that the snapshot can contain PPID cycles. This causes an infinite loop during the set construction and causes the git.exe command to hang. Testing found an instance where 3 processes were in a PPID cycle. The snapshot implied that each of these processes was its own great-grandparent. This should not be possible unless a PID was recycled and just happened to match up. For full disclosure, the Windows "System Idle Process" has PID and PPID 0. If it were to launch a Git command, it could cause a similar infinite loop. Or more properly, if any ancestor of the current Git command has PPID 0, it will appear to be a descendant of the idle process and trigger the problem. This commit fixes both cases by maintaining a list of the PIDs seen during the ancestor walk and stopping if a cycle is detected. Additionally, code was added to truncate the search after a reasonable fixed depth limit. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
1 parent 5c7bc00 commit dcbd5a1

File tree

1 file changed

+25
-7
lines changed

1 file changed

+25
-7
lines changed

compat/win32/trace2_win32_process_info.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <Psapi.h>
44
#include <tlHelp32.h>
55

6+
#define NR_PIDS_LIMIT 42
7+
68
/*
79
* Find the process data for the given PID in the given snapshot
810
* and update the PROCESSENTRY32 data.
@@ -21,13 +23,17 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
2123
}
2224

2325
/*
24-
* Accumulate JSON array:
26+
* Accumulate JSON array of our parent processes:
2527
* [
2628
* exe-name-parent,
2729
* exe-name-grand-parent,
2830
* ...
2931
* ]
3032
*
33+
* We artificially limit this to NR_PIDS_LIMIT to quickly guard against cycles
34+
* in the parent PIDs without a lot of fuss and because we just want some
35+
* context and don't need an absolute answer.
36+
*
3137
* Note: we only report the filename of the process executable; the
3238
* only way to get its full pathname is to use OpenProcess()
3339
* and GetModuleFileNameEx() or QueryfullProcessImageName()
@@ -38,16 +44,28 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
3844
{
3945
PROCESSENTRY32 pe32;
4046
DWORD pid;
47+
DWORD pid_list[NR_PIDS_LIMIT];
48+
int k, nr_pids = 0;
4149

4250
pid = GetCurrentProcessId();
51+
while (find_pid(pid, hSnapshot, &pe32)) {
52+
/* Only report parents. Omit self from the JSON output. */
53+
if (nr_pids)
54+
jw_array_string(jw, pe32.szExeFile);
4355

44-
/* We only want parent processes, so skip self. */
45-
if (!find_pid(pid, hSnapshot, &pe32))
46-
return;
47-
pid = pe32.th32ParentProcessID;
56+
/* Check for cycle in snapshot. (Yes, it happened.) */
57+
for (k = 0; k < nr_pids; k++)
58+
if (pid == pid_list[k]) {
59+
jw_array_string(jw, "(cycle)");
60+
return;
61+
}
4862

49-
while (find_pid(pid, hSnapshot, &pe32)) {
50-
jw_array_string(jw, pe32.szExeFile);
63+
if (nr_pids == NR_PIDS_LIMIT) {
64+
jw_array_string(jw, "(truncated)");
65+
return;
66+
}
67+
68+
pid_list[nr_pids++] = pid;
5169

5270
pid = pe32.th32ParentProcessID;
5371
}

0 commit comments

Comments
 (0)