* fix(cursor): own Windows background shells by launch Job membership (MUL-7417)
captureCursorBackgroundProcess on Windows walked a machine-wide PID->PPID
snapshot to prove a reported shell descends from the launch and to find
its pre-existing children. Windows keeps a child's recorded PPID after
the parent exits and reuses the PID, so the walk could reach a process
owned by someone else; opening it failed with 'Access is denied' or
'not an owned descendant' and the whole capture aborted. The daemon then
fell back to the idle watchdog and a Cursor run that later went quiet
took the 10-minute watchdog path instead of completing. In CI this was
TestCursorBackgroundLifecycle/multiple failing about 2 runs in 43.
Take candidates from the launch's own Job Object instead
(QueryInformationJobObject / JobObjectBasicProcessIdList). Descendants
inherit the Job at creation and cannot leave it, so membership is the
ownership proof: the parent-chain walk goes away, the PPID snapshot is
used only to attach a member's pre-existing children, and a member that
exits or is reused between listing and open is skipped instead of
failing the capture. Creation ordering still tells a real child from
one that points at a reused parent PID.
Restore TestCaptureCursorBackgroundLateDescendant on Windows; it was
excluded for exactly this race.
ci.yml runs the Windows Cursor step with -count=20 on this PR only to
show the race is gone; it is reverted before merge.
Co-authored-by: multica-agent <[email protected]>
* ci: restore -count=1 on the Windows Cursor background step
The -count=20 stress run on this PR passed (run 35580440594, windows-execenv);
the per-merge step goes back to a single pass.
Co-authored-by: multica-agent <[email protected]>
* fix(cursor): complete the Job member list and refuse unopenable live members (MUL-7417)
Review follow-ups on #8638:
- cursorJobProcessIDs grew its buffer only on ERROR_MORE_DATA. The kernel
can also return success with NumberOfProcessIdsInList short of
NumberOfAssignedProcesses, which a Job over the initial capacity or one
growing during the query would hit; the short list then passed for the
membership and could reject the target or leave children out. Retry
until the counts agree, fail explicitly if they never do, and make the
initial capacity a package var so a three-process tree exercises the
growth path in a test.
- The child walk skipped every open failure as 'exited or reused'. A live
member whose DACL denies PROCESS_SET_QUOTA|PROCESS_TERMINATE also fails
to open, and skipping it left the capture reporting success with that
member outside the tool Job. Skip only PIDs proven to carry no member
(handle outside the launch Job, or no longer listed after a re-query,
confirmed by one more open); otherwise fail the capture so the caller
takes its existing watchdog fallback. OpenProcess goes through a
package var so a test can make a live member unopenable.
Windows-only tests cover both, plus the plain claim-and-terminate path
for a shell with a pre-existing child.
Co-authored-by: multica-agent <[email protected]>
* test(cursor): name the Job list growth test so the Windows CI filter runs it
The windows-execenv step selects 'TestCursorBackground|TestCaptureCursorBackground';
TestCursorJobProcessIDsGrowsPastInitialCapacity matched neither and never ran.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>