Skip to content

fix: plugin runtime hardening — group kill, cleanup, interpreter re-verify, Initialize bounds (#294) - #300

Merged
DanielNetzer merged 1 commit into
mainfrom
forgecell/issue-294-d1915b0fa4156a431c43db0d
Oct 31, 2026
Merged

DanielNetzer merged 1 commit into
mainfrom
forgecell/issue-294-d1915b0fa4156a431c43db0d

Conversation

@DanielNetzer

Copy link
Copy Markdown
Owner

Implements issue #294.

Molecule: mol-294-640ebed9458ff7d1093a81f4
Formula: forgecell-github-issue
Base: d26c94b9c86b343d8e71451d03de66aae04bbe05
Delivery: 13809d053b8af01165ee274bef346cfc0314abd3

Awaiting exact-commit CI and human review. Harness-reported checks are not independent CI evidence. No merge, deployment or issue closure is authorized.

@DanielNetzer

Copy link
Copy Markdown
Owner Author

Adversarial review (plugin runtime hardening): one correctness MEDIUM, fixed in this PR with one verify pass. On macOS, waitid(WEXITED|WNOWAIT) can return for a leader that is only stopped, so the group kill could hit a live plugin. waitTerminated now sends the group kill only when si_code is CLD_EXITED, CLD_KILLED or CLD_DUMPED (1–3), and a seam-driven test covers the exited, killed and stopped cases. In the same round, Initialize now compares the server's raw contract (InitializeContract). A contract padded with Cf or bidi characters is refused, and only a sanitized copy is kept for reporting.

There is one by-design base-test change. TestClientIgnoresABoundedNumberOfNotifications scripted protocolVersion: "x", which is now refused. It is declared as an expected protected delta.

LOWs, for later:

  1. launch.go waitTerminated: with WNOWAIT, a stopped leader keeps reporting as stopped, so the loop busy-spins until the leader resumes or Shutdown kills it after the grace period. A short backoff on non-terminal codes would fix it.
  2. removeRunDir and unlockTree: the run dir is checked with Lstat and then opened with os.OpenRoot by path. Fix: require os.SameFile after OpenRoot. Exploiting this needs write access to the Lab temp root.
  3. The configure comment overstates when the group is signalled. os/exec's watchCtx can still Cancel in a tiny window after Process.Wait; Shutdown's own ordering is safe.
  4. The interpreter re-verify is a hash-by-path followed by exec-by-path. The remaining window can't be closed without fexecve, and the comment says so.
  5. Start's error path ignores the removeRunDir error, so a cleanup failure before any plugin has run is silent.

Integration: I applied this branch to current main (with #297). go vet ./... passes, and so does go test -timeout 20m ./internal/{plugin,safetext,forgesync,ui,tracker}/.... One earlier local run showed a conformance failure while the machine was heavily loaded. It didn't reproduce in 8 more runs, alone or combined. Watch CI.

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.

1 participant