[GH-ISSUE #1056] [Bug] Darwin ownership sweep fails captures on macOS 26 because KERN_PROCARGS2 withholds unrelated processes' environments #386

Closed
opened 2026-10-15 18:24:33 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @muhammad-luay on GitHub (Oct 8, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1056

Environment

  • macOS 26.3 (25D125), arm64; Node 24/26; REA 5.0.0, main b9fb438 (verified fix branch 148d200); verified both via the source tree and the compiled CLI.

Summary

On a busy macOS 26 host, capture_process_scenario and the capture-process CLI intermittently fail with cleanup_incomplete:

"owned_process_group":{"state":"unverified","reason":"process ownership token could not be read for N live process(es): environment_unavailable=1, sysctl_errno_22=2"}

even though no owned process remains.

Root cause

macOS 26 changed sysctl(KERN_PROCARGS2):

  • Environment blocks are stripped for Apple platform binaries (/bin, /usr/bin, /System, daemons) on any cross-process read; the same binary read from itself still exposes its environment.
  • Processes owned by another account now fail with EINVAL (errno 22).

Verified matrix on 26.3 (same behavior from ps eww): /bin/sleep, /bin/zsh, /usr/bin/ssh -> args only; Homebrew node/python -> full env; self-read -> full env. Independent reports: herdrdev/herdr#1821 (platform binaries), openclaw/openclaw#159112 (foreign uid EINVAL).

The residual ownership sweep examines every process created after the capture baseline and hard-fails any live candidate whose REA_PROCESS_RUN_ID cannot be read. Respawned unrelated processes (supervised ssh tunnels, shells, launchd helpers) therefore fail every capture on macOS 26.

Reproduction

  1. Run tests/boundary/mcp/sessionMcp.test.ts while a light churn load runs, e.g. for i in {1..60}; do env -i /bin/sleep 3 & sleep 0.5; done. The "records process residuals linked to capture Evidence" test fails deterministically on main; without a stressor it flakes in roughly one of two to three full --project mcp-boundary runs on a busy host.
  2. tests/boundary/cli/processCli.test.ts fails on the compiled base build (the documented capture CLI error is the same cleanup_incomplete).

CI is unaffected today because hosted macOS runners are macOS 15 and quiet; .nvmrc pins Node 24.18 while engines also admits >=26.

Impact

macOS 26 users see spurious capture failures on any busy machine; captures whose target execs a platform binary cannot be token-verified at cleanup at all, so ownership verification reports unverified even on a quiet host. This is not limited to tests.

Proposed direction (product decision)

A patch I have verified classifies sweep candidates instead of hard-failing all of them:

  • Darwin reader reports platform_binary_environment_withheld distinctly using csops(CS_OPS_STATUS); the process table carries the effective uid.
  • Unrelated candidates whose token is provably withheld (platform flag or foreign uid) or that exited during a bounded re-read/liveness confirmation are recorded as unverified evidence; related candidates and every other unreadable candidate stay fail-closed and are never signaled.

Open questions for maintainers: whether unverified should surface in cleanup_report/Unknowns (the success-path schema collapses owned_process_group to "verified" today), and whether a stricter or looser policy is preferred for same-uid, non-platform candidates that cannot be read at all.

I can open the PR with the patch, tests, and base reproduction.

Originally created by @muhammad-luay on GitHub (Oct 8, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1056 ## Environment - macOS 26.3 (25D125), arm64; Node 24/26; REA 5.0.0, main `b9fb438` (verified fix branch `148d200`); verified both via the source tree and the compiled CLI. ## Summary On a busy macOS 26 host, `capture_process_scenario` and the `capture-process` CLI intermittently fail with `cleanup_incomplete`: ``` "owned_process_group":{"state":"unverified","reason":"process ownership token could not be read for N live process(es): environment_unavailable=1, sysctl_errno_22=2"} ``` even though no owned process remains. ## Root cause macOS 26 changed `sysctl(KERN_PROCARGS2)`: - Environment blocks are stripped for **Apple platform binaries** (`/bin`, `/usr/bin`, `/System`, daemons) on any cross-process read; the same binary read from itself still exposes its environment. - Processes owned by **another account** now fail with `EINVAL` (errno 22). Verified matrix on 26.3 (same behavior from `ps eww`): `/bin/sleep`, `/bin/zsh`, `/usr/bin/ssh` -> args only; Homebrew `node`/`python` -> full env; self-read -> full env. Independent reports: herdrdev/herdr#1821 (platform binaries), openclaw/openclaw#159112 (foreign uid EINVAL). The residual ownership sweep examines every process created after the capture baseline and hard-fails any live candidate whose `REA_PROCESS_RUN_ID` cannot be read. Respawned unrelated processes (supervised ssh tunnels, shells, launchd helpers) therefore fail every capture on macOS 26. ## Reproduction 1. Run `tests/boundary/mcp/sessionMcp.test.ts` while a light churn load runs, e.g. `for i in {1..60}; do env -i /bin/sleep 3 & sleep 0.5; done`. The "records process residuals linked to capture Evidence" test fails deterministically on main; without a stressor it flakes in roughly one of two to three full `--project mcp-boundary` runs on a busy host. 2. `tests/boundary/cli/processCli.test.ts` fails on the compiled base build (the documented capture CLI error is the same `cleanup_incomplete`). CI is unaffected today because hosted macOS runners are macOS 15 and quiet; `.nvmrc` pins Node 24.18 while `engines` also admits >=26. ## Impact macOS 26 users see spurious capture failures on any busy machine; captures whose target execs a platform binary cannot be token-verified at cleanup at all, so ownership verification reports unverified even on a quiet host. This is not limited to tests. ## Proposed direction (product decision) A patch I have verified classifies sweep candidates instead of hard-failing all of them: - Darwin reader reports `platform_binary_environment_withheld` distinctly using `csops(CS_OPS_STATUS)`; the process table carries the effective uid. - Unrelated candidates whose token is provably withheld (platform flag or foreign uid) or that exited during a bounded re-read/liveness confirmation are recorded as unverified evidence; related candidates and every other unreadable candidate stay fail-closed and are never signaled. Open questions for maintainers: whether `unverified` should surface in `cleanup_report`/Unknowns (the success-path schema collapses `owned_process_group` to `"verified"` today), and whether a stricter or looser policy is preferred for same-uid, non-platform candidates that cannot be read at all. I can open the PR with the patch, tests, and base reproduction.
bsakaguchi 2026-10-15 18:24:33 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#386
No description provided.