Skip to content

Detect PID reuse in detached instance state - #2025

Open
waldekmastykarz wants to merge 3 commits into
dotnet:mainfrom
waldekmastykarz:pid-reuse-in-state
Open

waldekmastykarz wants to merge 3 commits into
dotnet:mainfrom
waldekmastykarz:pid-reuse-in-state

Conversation

@waldekmastykarz

Copy link
Copy Markdown
Collaborator

Closes #1755

Detached instances were considered alive if any process with the recorded PID existed. Because the OS recycles PIDs, a stale state record could look alive.

Changes

  • ProxyInstanceState now stores processStartTime (UTC), set when the state is written.
  • StateManager.IsInstanceAlive checks that the process exists and that its start time matches the recorded value (1s tolerance, because Linux derives start time from boot time). A mismatch means the PID was reused, so the record is treated as stale. It's used by state loading/pruning (stop, status, logs, api, LoadAllStatesAsync, FindSystemProxyInstanceAsync) and by GetOrphanedSystemProxyStatesAsync.
  • State files without the field (written by older versions) keep the PID-only check.
  • stop: the graceful-stop wait counts a reused PID as exited, and --force checks the start time before Kill(), so it never kills an unrelated process.
  • logs uses the recorded start time when it's available.

Validation

  • New StateManagerPidReuseTests; DevProxy.Tests passes (138/138).
  • Detached smoke run: the state file contains processStartTime. After the start time was edited in the file, status reported the instance as not running and pruned the record, and stop --force --pid left the live process running.

Persist the process start time in the instance state file and verify it
before treating a PID as a live Dev Proxy instance. A mismatch means the
OS recycled the PID and the record is stale. State files written by older
versions (without the start time) keep the previous PID-only behavior.

stop no longer waits on or force-kills an unrelated process that reused
the PID, and logs uses the recorded start time.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 31, 2026 08:59
@waldekmastykarz
waldekmastykarz requested a review from a team as a code owner October 31, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved process-identity handling can allow forced termination of an unverified process and deletion of a live instance’s authentication token when a PID is reused.

2 open issues
What changed in this PR

This PR addresses #1755 by recording process start times to distinguish detached Dev Proxy instances from unrelated processes that reuse their PIDs.

Changes:

  • Adds start-time checks to instance liveness and stale-state handling, preserving PID-only behavior for legacy records.
  • Applies identity checks during graceful and forced stops, and uses recorded start times for logs.
  • Adds tests for serialization, identity matching, legacy compatibility, and log start times.
File Description
DevProxy/​State/​StateManager.cs Adds process identity checks to liveness and stale-state detection.
DevProxy/​State/​ProxyInstanceState.cs Adds the optional processStartTime field.
DevProxy/​Commands/​StopCommand.cs Checks process identity while waiting and before forced termination.
DevProxy/​Commands/​LogsCommand.cs Prefers the recorded process start time.
DevProxy/​Commands/​DevProxyCommand.cs Records process start time when saving instance state.
DevProxy.Tests/​StateManagerPidReuseTests.cs Tests process identity handling and backward compatibility.

Review effort: Balanced

Comment thread DevProxy/State/StateManager.cs Resolved
Comment thread DevProxy/State/StateManager.cs Resolved
- stop --force refuses to terminate a process whose start time can't be
  read when the state recorded one.
- PID-keyed API tokens are only deleted when no process with that PID is
  running, so a new instance that reused the PID keeps its token.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical instance-identity races can delete a replacement instance’s state and disable its system proxy while leaving its process running.

2 open issues
2 resolved since last review

Review effort: Balanced

Comment thread DevProxy/Commands/StopCommand.cs Resolved
Comment thread DevProxy/State/StateManager.cs Resolved
- stop --force on an instance that's already gone (exited or PID reused)
  restores the system proxy only when no live instance owns it.
- State records are re-read before deletion and removed only if they
  still describe the same instance (PID + start time).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

There are no active review issues for the process-identity changes.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

State/liveness keyed on PID alone is vulnerable to PID reuse

2 participants