Skip to content

[Bug]: rootless container with an absolute cgroupsPath stays in the caller's cgroup; delete --force then exits 0 and leaves it running #3794

Open

Description

@ItzExotical

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 ps lists no processes,
  • youki delete --force exits 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 to cgroup.procs is logged at debug level ("leaving process in its parent cgroup") and returns Ok(()). :145-151 does the same for create_dir.
  • crates/libcontainer/src/process/container_intermediate_process.rs:55-58 sets with_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 --force calls do_kill(SIGKILL, true) (crates/libcontainer/src/container/container_delete.rs:56). kill_all_processes signals only what get_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 300

With "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
1

Control: 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:

  1. Narrow the rootless tolerance to what runc allows (opencontainers/cgroups fs2/fs2.go, Manager.Apply: ErrRootless only when config.Path == "" and no controllers are needed; a failed cgroup.procs write is always an error). In youki that means passing to the v2 manager whether linux.cgroupsPath was set and whether limits were requested, and returning the error from create_unified_cgroup/attach_pid otherwise. This keeps the container-in-container case from bug: rootless containers inside of docker error with cgroup.subtree_controll #3597 (no path, no limits) working.
  2. 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 makes delete --force reliable 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

  1. r14dd commented on Oct 7, 2026

    @r14dd
    Contributor

    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.

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