fix: Limit Unix IPC endpoints to the current OS user

Merged
HA
3mo ago
+1601-27

Summary

  • Give each OS user a private Unix IPC socket directory.
  • Validate the endpoint only at the single client dial boundary, so project-only dispatcher commands do not require Unity to be running.

Previous behavior and root cause

All Unix clients used a shared /tmp/uloop directory. Another OS user could precreate or replace endpoint paths before the intended Unity process used them. The initial fix validated while resolving a project, but resolution also serves pin, cache, help, and version-forwarding commands that never dial Unity.

Fix and attack conditions

C# and Go derive the same /tmp/uloop-UID endpoint. They validate the sticky root-owned /tmp target, require a real owner-only 0700 endpoint directory, and refuse symlinks, wrong ownership, special bits, and unsafe stale paths. Socket permissions are verified as 0600 after bind.

The Go client now has one dial choke point. On Unix it validates the same endpoint policy immediately before net.Dial; a missing private directory is typed as Unity not running and never attempts a dial. Windows enters the same choke point with a no-op validator, preserving named-pipe behavior.

The Unix stat ABI is selected explicitly for macOS x64, macOS arm64, and glibc Linux x64/arm64. Unknown ABI layouts fail closed.

Verification

  • Focused Go tests: go test ./project ./unityipc ./ipcendpoint ./errors
  • Dispatcher Go tests: go test ./...
  • scripts/check-go-cli.sh
  • Unity compile: 0 errors, 0 warnings
  • Unix endpoint/listener EditMode tests: 31 passed
  • Windows owner-only named-pipe tests: 3 skipped on macOS as expected
  • TestProtocolVersionMatchesUnityPackage
  • Release trigger guard
  • Windows and Linux Go project cross-compilation
  • Exact-head CI, d89b9f25d0bba6f48a92d9505baeb83d59dd9878:

Protocol release ordering

This PR changes the IPC rendezvous contract and advances protocolVersion from 3 to 4. The PR-diff-equivalent local invocation of check-protocol-minimum-version intentionally fails until a project runner release advertising protocol 4 is published. The manually dispatched exact-head CI has no pull-request diff and therefore skips that diff-dependent reminder; it is not evidence of a passing minimum-version check.

After the runner release, release-please must advance the package pin to the published runner version before the Unity package release proceeds.

Residual risk

The design protects against other OS users in the shared temporary parent. A process running as the same OS user can still manipulate that user's private directory, which is outside this multi-user boundary.