Skip to content

Implement SafeProcessHandle.Open API#126705

Draft
Copilot wants to merge 9 commits into
mainfrom
copilot/add-safeproceshandle-open-api
Draft

Implement SafeProcessHandle.Open API#126705
Copilot wants to merge 9 commits into
mainfrom
copilot/add-safeproceshandle-open-api

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

Description

Implements the SafeProcessHandle.Open(int processId) API that opens a handle to an existing process by its ID.

Changes

Native layer (pal_process.c, pal_process.h, pal_process_wasi.c):

  • Added TryOpenPidfd linux-specific helper that uses pidfd_open syscall (works on both Linux and Android since both define __linux__)
  • Extended SystemNative_ForkAndExecProcess with int32_t* outPidfd output parameter
  • Added SystemNative_OpenProcess that validates the process exists via kill(pid, 0) and attempts to obtain a pidfd

Managed layer:

  • Interop.ForkAndExecProcess.cs: Added out int lpPidfd parameter to both the managed wrapper and P/Invoke declaration
  • SafeProcessHandle.Unix.cs: OpenCore creates a ProcessWaitState.Holder for the target process (ensuring wait/kill operations work on the returned handle) and stores the pidfd when available
  • SafeProcessHandle.Windows.cs: OpenCore uses OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE
  • SafeProcessHandle.cs: Public Open(int processId) method with argument validation

Tests (SafeProcessHandleTests.cs):

  • Open_InvalidProcessId_ThrowsArgumentOutOfRangeException — validates argument checking
  • Open_NonExistentProcessId_ThrowsWin32Exception — validates error for dead PIDs
  • Open_RunningProcess_ReturnsValidHandle — validates handle properties
  • Open_ThenKill_TerminatesProcess — validates kill + wait via the opened handle

Design decisions

  • On Unix, _waitStateHolder is always present in SafeProcessHandle, enabling wait/kill operations on handles obtained via Open
  • On platforms without pidfd (e.g., macOS), int.MinValue is used as the handle value to avoid IsInvalid returning true
  • The pidfd is stored in _pidfd field and closed in ReleaseHandle

Testing

All 617 process tests pass (0 failures, 5 skipped).

Copilot AI and others added 2 commits April 9, 2026 11:51
Copilot AI self-assigned this Apr 9, 2026
Copilot AI review requested due to automatic review settings April 9, 2026 12:38
Copilot AI review requested due to automatic review settings April 9, 2026 12:38
Copy link
Copy Markdown
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

@copilot give my feedback a try. Run all process tests and ensure they pass before you push any changes.

Comment thread src/native/libs/System.Native/pal_process.c Outdated
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-diagnostics-process
See info in area-owners.md if you want to be subscribed.

…ss with outPidfd parameter

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/664957d4-993f-4f40-aa79-ce45ec55fd11

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 9, 2026 13:26
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 9, 2026

@copilot give my feedback a try. Run all process tests and ensure they pass before you push any changes.

Done in e2bc39d. All 405 process tests pass (0 failures, 5 skipped).

Copilot AI requested a review from adamsitnik April 9, 2026 13:44
Copy link
Copy Markdown
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

@copilot address my feedback

Comment thread src/libraries/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs Outdated
Comment thread src/libraries/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs Outdated
Comment thread src/native/libs/System.Native/pal_process.c Outdated
…acOS posix_spawn path

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ed9ebae7-b0ac-4a66-a222-8f7305c55a9b

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 9, 2026 14:36
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 9, 2026

@copilot address my feedback

All three review items addressed in a114b09. All 405 process tests pass (0 failures, 5 skipped).

… use int.MinValue for non-pidfd handles

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/99759f32-cf6d-48fa-9f60-1ec4f2630ad2

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 10, 2026 10:46
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 10, 2026

@copilot address my feedback

All four review items addressed in b63ecec. All 406 process tests pass (0 failures, 5 skipped).

Copilot AI and others added 2 commits May 22, 2026 14:30
…shandle-open-api

# Conflicts:
#	src/libraries/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs
#	src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs
#	src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
#	src/libraries/System.Diagnostics.Process/tests/SafeProcessHandleTests.cs
#	src/native/libs/System.Native/pal_process.c
#	src/native/libs/System.Native/pal_process.h
#	src/native/libs/System.Native/pal_process_wasi.c

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot May 22, 2026 14:37
Copy link
Copy Markdown
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

@copilot please address my feedback

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void Open_RunningProcess_ReturnsValidHandle()
{
Process process = CreateProcess(static () =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Process process = CreateProcess(static () =>
using Process process = CreateProcess(static () =>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9fd4c33.

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void Open_ThenKill_TerminatesProcess()
{
Process process = CreateProcess(static () =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Process process = CreateProcess(static () =>
using Process process = CreateProcess(static () =>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9fd4c33.

using SafeProcessHandle handle = SafeProcessHandle.Open(process.Id);
handle.Kill();

Assert.True(process.WaitForExit(WaitInMS));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We need to use handle here as well

Suggested change
Assert.True(process.WaitForExit(WaitInMS));
Assert.True(handle.WaitForExit(WaitInMS));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9fd4c33. Used handle.TryWaitForExit(TimeSpan.FromMilliseconds(WaitInMS), out _) since SafeProcessHandle doesn't have a WaitForExit(int) overload.

Comment on lines +352 to +353
#if defined(__linux__)
int pidfd = (int)syscall(SYS_pidfd_open, pid, 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@copilot is SYS_pidfd_open available on Android as well? If so, we should do it for both platforms

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, SYS_pidfd_open is available on Android. The __linux__ macro is defined on Android, so both the syscall number definitions (lines 43-56) and the TryOpenPidfd function body (line 352) already cover Android — no change needed.

throw new Win32Exception();
}

return new SafeProcessHandle(pidfd != -1 ? pidfd : int.MinValue, processId);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The SafeProcessHandle that is created here can't be used for waiting for the process to exit. We need to obtain the wait state holder for given process

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9fd4c33. OpenCore now creates a ProcessWaitState.Holder for the process, which enables wait operations on the returned handle.

Validate();

if (_waitStateHolder is null)
if (_waitStateHolder is not null)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

_waitStateHolder must always be present

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9fd4c33. Removed the _processId field and the private (int pidfd, int processId) constructor. OpenCore now uses new ProcessWaitState.Holder(processId) + the standard constructor, so _waitStateHolder is always present.

Copilot AI requested review from Copilot and removed request for Copilot May 22, 2026 16:16
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 22, 2026

@copilot please address my feedback

All feedback addressed in 9fd4c33. Key changes:

  • OpenCore now creates a ProcessWaitState.Holder for the process, ensuring _waitStateHolder is always present
  • Removed the _processId field and private pidfd constructor — ProcessId always comes from _waitStateHolder
  • Tests use using Process and handle.TryWaitForExit instead of process.WaitForExit
  • TryOpenPidfd already works on Android since __linux__ covers it (the SYS_pidfd_open definitions are under #if defined(__linux__))

All 617 process tests pass (0 failures, 5 skipped).

Copilot AI requested a review from adamsitnik May 22, 2026 16:17
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3954e392-7975-49be-969f-eceeacf7b568

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot May 22, 2026 17:06
Copilot AI requested a review from adamsitnik May 22, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Diagnostics.Process NO-REVIEW Experimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants