Skip to content

Contained commands can move themselves out of their invocation cgroup #79

Description

@queso

Follow-up from the CodeRabbit review on #78 (thread).

Problem

Per-invocation cgroup containment (#77, src/worker/cgroup-containment.ts) creates conduit-<pid>-<n> under the cgroup the kernel runs in, and requires the kernel's user to be able to write that parent cgroup. Station and harness commands run as that same user. Under cgroup v2, a process may migrate to any cgroup whose cgroup.procs it can write, provided it can also write the cgroup.procs of the common ancestor. Here the parent is that common ancestor, and it is writable. So a command can run echo $$ > <parent>/cgroup.procs, leave its invocation's cgroup, and survive cgroup.kill on timeout or after exit.

Commands that detach the ordinary way (setsid, nohup, a daemonizing fork, which is what Claude Code's Bash tool does) do not change cgroup and are still killed. The gap is only a command that sets out to evade containment. #78 narrowed the documented contract to say so (docs/harness-containment.md, "Process-tree termination") and names the container as the boundary for that case.

Possible fix

Manage cgroups under an identity the contained commands do not share, so the parent's cgroup.procs is not writable by them while the spawn wrapper can still join the child cgroup. Options to evaluate:

  • run station and harness commands as a separate, unprivileged user, with the kernel's user owning the delegated subtree;
  • have a small privileged helper, or systemd (systemd-run --user --scope), create and populate the invocation cgroup so the kernel's user never needs write access to the parent;
  • use a cgroup namespace per invocation (unshare --cgroup) so the command cannot see the parent. Check whether that is enough: namespacing hides the path but does not by itself remove write permission.

Whichever approach is chosen, add a conformance scenario to src/worker/harness-containment.conformance.ts in which the fixture writes its own pid into the parent cgroup.procs, and assert it is still killed. Then restore the stronger wording in docs/harness-containment.md and the module header.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions