Repository navigation
[Bug]: rootless container with an absolute cgroupsPath stays in the caller's cgroup; delete --force then exits 0 and leaves it running #3794
Description
Bug Description
A rootless container whose linux.cgroupsPath is absolute is created successfully even when youki cannot move it into that cgroup. The process stays in the caller's cgroup (for example an SSH or TTY session-N.scope), and the requested cgroup is created but empty. After that:
youki pslists no processes,youki delete --forceexits 0, removes the state and the cgroup, and leaves the container process running, now untracked,- resource limits are written to the empty cgroup and limit nothing.
crun refuses to create the same container (write to .../cgroup.procs: Permission denied). runc does the same: rootless permission errors are tolerated only when there is no cgroupsPath and no limits.
Cause: since #3609 the cgroup v2 manager ignores EACCES/EROFS when rootless is set, both when creating the directory and when writing cgroup.procs:
crates/libcgroups/src/v2/manager.rs:166-181(attach_pid): a failed write tocgroup.procsis logged at debug level ("leaving process in its parent cgroup") and returnsOk(()).:145-151does the same forcreate_dir.crates/libcontainer/src/process/container_intermediate_process.rs:55-58setswith_rootless(rootless_required())for every rootless container, whether or not the spec names a cgroup or asks for limits.- An absolute path always picks the v2 manager (
crates/libcgroups/src/common.rs:374), so the user-namespace rule that forces the systemd manager does not apply here. delete --forcecallsdo_kill(SIGKILL, true)(crates/libcontainer/src/container/container_delete.rs:56).kill_all_processessignals only whatget_all_pids()finds in the container's cgroup (container_kill.rs:117). The cgroup is empty, so nothing is killed, the state moves to Stopped,remove()deletes the empty cgroup, and the state directory is removed.
This is separate from #3792 (useSystemd not recorded for a user namespace). That fix does not change this case: the v2 manager is used at create and at delete alike.
Steps to Reproduce
As an unprivileged user (uid 1000) on cgroup v2, from a shell whose cgroup is outside user@1000.service (here 0::/user.slice/user-1000.slice/session-4.scope, an ordinary login session). Unpack the Alpine minirootfs alpine-minirootfs-3.24.2-x86_64.tar.gz (sha256 c5ca053cfe1d85c5b96dff8b9bc57045f7f184a30ffb6b65776409ca90388677) into bundle/rootfs, then bundle/config.json:
{
"ociVersion": "1.0.2",
"process": {
"user": { "uid": 0, "gid": 0 },
"args": ["sleep", "300"],
"env": ["PATH=/usr/bin:/bin"],
"cwd": "/"
},
"root": { "path": "rootfs" },
"mounts": [
{ "destination": "/proc", "type": "proc", "source": "proc" },
{ "destination": "/dev", "type": "tmpfs", "source": "tmpfs" }
],
"linux": {
"namespaces": [{ "type": "user" }, { "type": "pid" }, { "type": "mount" }],
"uidMappings": [{ "containerID": 0, "hostID": 1000, "size": 1 }],
"gidMappings": [{ "containerID": 0, "hostID": 1000, "size": 1 }],
"cgroupsPath": "/user.slice/user-1000.slice/user@1000.service/user.slice/yk-abs"
}
}youki (main, c8a4021):
$ cd bundle
$ youki --root $PWD/../state --log-level debug --log $PWD/../run.log run -d yk; echo $?
0
$ grep 'rootless cgroup' ../run.log
DEBUG libcgroups::v2::manager: rootless cgroup: cannot move process into "/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/user.slice/yk-abs": failed to write 2001895 to /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/user.slice/yk-abs/cgroup.procs: Permission denied (os error 13); leaving process in its parent cgroup
$ P=$(youki --root $PWD/../state state yk | jq .pid)
$ cat /proc/$P/cgroup
0::/user.slice/user-1000.slice/session-4.scope
$ wc -l < /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/user.slice/yk-abs/cgroup.procs
0
$ youki --root $PWD/../state ps yk
UID PID PPID C STIME TTY TIME CMD
$ youki --root $PWD/../state delete --force yk; echo $?
0
$ youki --root $PWD/../state list
ID PID STATUS BUNDLE CREATED CREATOR
$ ps -o pid,cmd -p $P
PID CMD
2001898 sleep 300With "resources": { "pids": { "limit": 5 } } added to linux, run also exits 0, pids.max in yk-abs reads 5, and the process is still in session-4.scope, so the limit does not apply to it.
crun 1.30.1, same bundle:
$ crun --root $PWD/../state-crun run -d yk; echo $?
write to `/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/user.slice/yk-abs/cgroup.procs`: Permission denied
1Control: the same youki command started from inside the user manager (systemd-run --user --scope -q youki ... run -d yk) joins yk-abs, and delete --force kills the process.
Results:
run |
process cgroup | delete --force |
|
|---|---|---|---|
| youki main (c8a4021) | exit 0 | session-4.scope |
exit 0, state gone, process still running |
| youki v0.7.0 release binary | exit 0 | session-4.scope |
exit 0, state gone, process still running |
youki main + the useSystemd fix from #3792 |
exit 0 | session-4.scope |
exit 0, state gone, process still running |
| crun 1.30.1 | exit 1, Permission denied |
(not created) | (nothing to delete) |
youki main, started inside user@1000.service |
exit 0 | yk-abs |
exit 0, process killed |
Expectation
Either run/create fails, as crun and runc do, when the explicitly requested cgroup cannot be joined, or the container is tracked so that delete --force still kills it. delete --force must not report success while the container process keeps running.
System and Setup Info
youki version: 0.7.0
commit: 0.7.0-c8a40215e459345beed9fe9e5ece211908ed239b
spec: 1.1.0
Kernel-Release 7.2.8-1-cachyos
Operating System CachyOS
Cgroup setup unified
The v0.7.0 release binary (0.7.0-94ba653e) behaves the same. crun 1.30.1, systemd 262, rootless, user 1000.
Additional Context
Possible fixes, either or both:
- Narrow the rootless tolerance to what runc allows (
opencontainers/cgroupsfs2/fs2.go,Manager.Apply:ErrRootlessonly whenconfig.Path == ""and no controllers are needed; a failedcgroup.procswrite is always an error). In youki that means passing to the v2 manager whetherlinux.cgroupsPathwas set and whether limits were requested, and returning the error fromcreate_unified_cgroup/attach_pidotherwise. This keeps the container-in-container case from bug: rootless containers inside of docker error withcgroup.subtree_controll#3597 (no path, no limits) working. - In
kill_all_processes, also signal the init pid from the state when it is not among the cgroup's pids. With its own PID namespace, killing init kills the rest. This makesdelete --forcereliable whatever the cgroup state.
Related, not duplicates: #3609 (added the tolerance), #3597 (its motivating issue), #3690 (cgroupfs + rootless differences with runc, about extra pids in cgroup.procs).
This report was investigated and written with help from an AI assistant (Claude). I reproduced every command and output above on my machine.
Activity
r14dd commentedon Oct 7, 2026
I'd like to take this one. Plan is to only tolerate the rootless EACCES/EROFS when the spec has no cgroupsPath and no resource limits, like runc, and fail create otherwise. Will have a PR up soon.