Chaos drill 0.C3 — fork-bomb a wall you built¶
Claim — pids.max makes a fork bomb a local event: fork() returns EAGAIN inside the cgroup while the rest of the guest keeps forking normally. The blast radius stops at the cgroup boundary, and you can point at where.
Topology — bare, still up, still as root. This exercise is why P0 gets its own guest. Run it on anything shared and you will take that machine out; run it here and the worst case is one tofu apply.
Footprint note — set pids.max before you fork, in the same shell session, and verify it with a read-back. A fork bomb outside the cgroup will exhaust the guest's global kernel.pid_max and you will not get a shell back. Have a second SSH session already open before step 3.
Do
mkdir /sys/fs/cgroup/forkers, with+pidsdelegated from the root (cat /sys/fs/cgroup/cgroup.subtree_control— add it there if missing). Do not write intoforkers/cgroup.subtree_control: that would makeforkersa non-leaf and step 3's shell move would fail withEBUSY, as in the OOM drill.echo 32 > /sys/fs/cgroup/forkers/pids.max. Read it back. Do not proceed until it reads32.- In your second session, start a dedicated shell (
bash) rather than moving the login shell out of its systemd session scope, then inside itecho $$ > /sys/fs/cgroup/forkers/cgroup.procs, confirm withcat /proc/self/cgroup, then run a bounded forker first:for i in $(seq 1 64); do sleep 300 & done. - Read the errors. Read
pids.currentandpids.events. - Now the unbounded version, still inside the cgroup:
:(){ :|:& };:. Watch it fail to take the machine down. - From the first session — which is outside the cgroup — confirm you can still fork:
ls,ps aux | wc -l. - Kill the cgroup's processes wholesale:
cat /sys/fs/cgroup/forkers/cgroup.procs | xargs -r kill -9, or write1tocgroup.killif the kernel provides it.
Observe
cat /sys/fs/cgroup/forkers/pids.current /sys/fs/cgroup/forkers/pids.max
cat /sys/fs/cgroup/forkers/pids.events # the `max` counter
cat /sys/fs/cgroup/forkers/cgroup.procs | wc -l
Expect — the shell prints fork: retry: Resource temporarily unavailable (EAGAIN), pids.current sits pinned at 32, and pids.events' max counter climbs once per refused fork. The first session is unaffected throughout. cgroup.kill — write 1, every process in the subtree dies — is the tidiest way out, and is exactly the primitive a runtime uses to stop a container that will not stop itself.
Write down — the errno, the counter, and one sentence on why pids.max is a containment limit rather than a performance one: it does not make anything faster, it makes one workload unable to deny service to the rest of the box.
Teardown — confirm pids.current is 0, then rmdir /sys/fs/cgroup/forkers. Check pgrep -c sleep is zero before moving on; strays here will confuse the network module. Guest stays up.