Skip to content

mutate: each test process in a cgroup of its own when the run was delegated one - #48

Merged
aledbf merged 1 commit into
mainfrom
mutate-cgroup
Oct 22, 2026
Merged

aledbf merged 1 commit into
mainfrom
mutate-cgroup

Conversation

@aledbf

@aledbf aledbf commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

On 2026-10-22 a task mutate run hung a 30 GiB workstation: the kernel killed thousands of importer-4.test processes of 20-50 MiB each. One binary was starting itself every time it started. The /proc watch measures one process tree, and each process left the tree when its parent died, so the tree never held much.

  • Run in a cgroup that systemd delegated (systemd-run --user --scope -p Delegate=yes), mutate moves its own processes to a child cgroup. It then starts each test process in a cgroup of its own, using clone3 into the cgroup:
    • memory.max at the test's share, and no swap.
    • pids.max 1024; once a fork is refused there, the whole group is stopped.
    • Whatever is still running when the test ends is killed, and the cgroup is removed. This also ends the wait on a pipe that a grandchild held open.
  • Anywhere else it measures /proc as before. The run prints which mode it is in.
  • TestATestProcessIsHeldByItsCgroup re-runs itself in a delegated scope. In that scope it covers memory, a fork bomb, a leftover child and cleanup, and it skips when there is no user manager. A mutate run does not see coverage from that process, so the cgroup functions carry mutate-lane markers naming the test. I planted mutants in each of them by hand, and the test refused every one.

Checked: task test, task lint, and go run ./cmd/mutate -max 200 in a delegated scope (28 refused, 0 survived).

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…egated one

On 2026-10-22 a mutate run took a 30 GiB workstation until it hung: thousands
of importer-4.test processes of 20-50 MiB each, a binary that started itself
each time it started. The /proc watch measures one process tree, and each
process left the tree when its parent died, so the tree never held much.

Run in a cgroup systemd delegated it (systemd-run --user --scope -p
Delegate=yes), mutate moves its own processes to a child and starts each test
process in a child of its own (clone3 into the cgroup): memory.max at its
share, no swap, pids.max 1024, a fork refused there stops the group, and
what is left when the test ends is killed and the cgroup removed - which also
ends the wait on a pipe a grandchild held open. Anywhere else it measures
/proc as before, and the run says which it does.

The cgroup code runs in a test that re-runs itself in a delegated scope, a
process whose coverage a mutate run does not see; its functions carry
mutate-lane markers naming that test, and planted mutants of each were
refused by it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@aledbf
aledbf merged commit f6269c6 into main Oct 22, 2026
1 check passed
@aledbf
aledbf deleted the mutate-cgroup branch October 22, 2026 15:17
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