Skip to content

fix(process): unify subprocess ownership and child lifecycle safety - #614

Open
somethingwithproof wants to merge 15 commits into
Cacti:developfrom
somethingwithproof:fix/subprocess-ownership
Open

fix(process): unify subprocess ownership and child lifecycle safety#614
somethingwithproof wants to merge 15 commits into
Cacti:developfrom
somethingwithproof:fix/subprocess-ownership

Conversation

@somethingwithproof

@somethingwithproof somethingwithproof commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

  • make subprocess-registry detach atomic so exactly one closer owns each entry
  • make cancellation cleanup release only the detached node it owns
  • replace vfork/execve child setup with posix_spawn and explicit file actions
  • mark every pipe and stdio-collision duplicate close-on-exec
  • restore the child's default SIGPIPE disposition while preserving the parent's handling
  • bound TERM/KILL reaping and park unreaped children in a fixed-capacity sweep list
  • harden PHP script-server slot ownership, startup, restart, signal, descriptor, and cleanup paths
  • retire and restart failed or oversized reads so stale response tails cannot poison later data sources
  • revalidate every command retry and cap recovery handshakes at two seconds

This folds the child-process lifecycle portion of #597 into the narrower ownership PR. It does not absorb unrelated poll-host, SQL, SNMP, ICMP, or build-infrastructure work.

Review-driven corrections

  • close-on-exec is explicitly restored after dup() in both read and write stdio-collision branches
  • the source invariant check now uses portable POSIX character classes
  • direct tests cover duplicate flags, both collision branches, failure after pipe creation, abandoned-child sweeping, and capacity overflow
  • collision cleanup preserves current-child redirects even when an older registry entry occupies fd 0 or 1
  • script children receive SIGTERM before SIGKILL and unreaped PHP children transfer to the bounded sweep registry
  • the Docker integration suite now starts a real PHP script server, verifies its startup protocol and database result, and checks that shutdown leaves no zombie PHP process

Validation

  • Ubuntu 24.04 container with GCC and GNU ld: make check, 6/6 targets passed with no skips, including test_php_runtime
  • real PHP script-server Docker integration: 6/6 checks passed; exact value 42 reached poller_output and no zombie remained
  • macOS Homebrew dependency build passed
  • macOS make check: 5/5 available binaries passed; GNU --wrap runtime suite is Linux-only
  • PHP fixture syntax checked through mise PHP 8.3
  • actionlint, shellcheck, and git diff --check passed

Closes #610.

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

The changes correctly enforce exclusive registry ownership under ListMutex, add targeted tests for the new invariants (including cancellation), and include a safety guard against dangerous kill() PID values.

Pull request overview

This PR hardens nft_pclose()’s subprocess registry handling so that closing a nft_popen() descriptor transfers exclusive ownership to exactly one closer, preventing duplicate close/wait/free races and aligning behavior with issue #610’s acceptance criteria.

Changes:

  • Refactors nft_pclose() to atomically detach+close the registry entry under ListMutex and free only the detached node via the cleanup handler.
  • Adds unit tests that exercise concurrent close ownership, cancellation cleanup behavior, and preservation of pthread cancellation state on early EBADF.
  • Guards the timeout kill path in exec_poll() against invalid/unsafe PIDs and records the fix in CHANGELOG.
File summaries
File Description
tests/unit/test_linked.c Adds concurrency/cancellation-focused unit tests for nft_pclose()/registry ownership semantics.
poller.c Prevents kill() from being called with invalid PIDs on NIFTY POPEN timeout.
nft_popen.c Introduces atomic detach-and-take helper and updates nft_pclose() cleanup ownership model.
CHANGELOG Documents issue #610 fix.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Detach the subprocess entry under ListMutex before exposing it to close, wait, or cancellation cleanup. A second closer now receives EBADF instead of sharing a pointer that can be freed concurrently.

Keep cancellation disabled through descriptor close so cleanup cannot close a reused descriptor number. Cover concurrent closers and cancellation against the shipped nft_popen object.

Closes Cacti#610

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
PR Cacti#542 removed both while adding unrelated poller fixes; its branch predated
only guard was a shell script that grepped the source, and it was deleted in
the same commit.

An inherited pipe write end keeps a script's reader from seeing EOF, so the
thread blocks to script_timeout for a device that answered. The unbounded
waitpid has no timeout at all, and the thread holds its available_scripts token
while it waits.

The php.c reap survived Cacti#542 and is better than what was removed, so only its
pipes needed the flag. Both callers now share one helper, covered by six tests
against the shipped object: two of them fail if the close-on-exec goes away
again.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Collect the child-process corrections from Cacti#597 after the foundational close-on-exec restoration. Preserve cancellation-safe abandoned-child handling, descriptor failure contracts, PHP resource ownership, startup restart bounds, and persistent SIGPIPE handling.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Carry the PHP and signal-handling review fixes from Cacti#597 while leaving nft_pclose ownership to focused PR Cacti#614. The remaining registry-specific review changes will be replayed only after Cacti#614 lands.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Extract the production-linked PHP runtime suite, its script-server fixture, the portable linker-wrap probe, and the structural regression guard from Cacti#597.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Share the reviewed spawn-attribute helper between nft_popen and the PHP script server without pulling the overlapping nft_pclose ownership rewrite from Cacti#597.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Map the BSD ICMP_UNREACH name to the Linux spelling used by the shared linked-object regression suite.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Check the shared close-on-exec helper and bounded PHP handshake without pulling the poll_host and ICMP assertions assigned to other replacement branches.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
@somethingwithproof somethingwithproof changed the title Fix subprocess registry ownership during close fix(process): unify subprocess ownership and child lifecycle safety Sep 6, 2026
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make nft_pclose take exclusive ownership of subprocess entries

5 participants