From 3facdd9ae96a26f437945d2c0d279d1ab49c265f Mon Sep 17 00:00:00 2001 From: George Katsitadze Date: Wed, 15 Jul 2026 14:24:41 -0700 Subject: [PATCH 1/2] fix: only remount /sys/fs/cgroup when rooted at a nested path The cgroup-v2 nesting setup in wrap_dockerd.sh unconditionally unmounted and remounted /sys/fs/cgroup after unsharing the cgroup namespace to ensure inner container cgroups land under the envbox container's cgroup on the host (see #169). On some runtimes, /sys/fs/cgroup is already rooted at "/" once the cgroup namespace is unshared, and remounting a live cgroupfs there can fail with EBUSY, aborting dockerd startup. This can break envbox startup in affected environments, including the README Hacking example. Read the mount root from /proc/self/mountinfo and skip the umount/remount when it's already "/", only remounting when the mount still points at a nested path from the inherited environment. Update the inline script copy and assertions in the docker tests to match. Refs: https://linear.app/codercom/issue/PLAT-383 --- cli/docker_test.go | 20 ++++++++++++++------ cli/wrap_dockerd.sh | 16 +++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cli/docker_test.go b/cli/docker_test.go index 4db427f..d51b8ea 100644 --- a/cli/docker_test.go +++ b/cli/docker_test.go @@ -191,11 +191,17 @@ func TestDocker(t *testing.T) { # (https://github.com/moby/moby/blob/8d9e3502aba39127e4d12196dae16d306f76993d/hack/dind#L61-L79), # bounded by envbox_max_attempts. if [ -f /sys/fs/cgroup/cgroup.controllers ]; then - # Remount /sys/fs/cgroup so the new cgroup namespace's view becomes the - # fs root; inner container cgroups end up under the envbox container's - # cgroup on the host. - umount /sys/fs/cgroup || { echo "envbox: failed to umount /sys/fs/cgroup" >&2; exit 1; } - mount -t cgroup2 cgroup /sys/fs/cgroup || { echo "envbox: failed to mount cgroup2 on /sys/fs/cgroup" >&2; exit 1; } + # Some runtimes already root /sys/fs/cgroup at "/" after unsharing the + # cgroup namespace; remounting it again fails with EBUSY. Only remount + # when it's still rooted at a nested host path. + cgroup_mount_root=$(awk '$5 == "/sys/fs/cgroup" { print $4; exit }' /proc/self/mountinfo) + if [ "$cgroup_mount_root" != "/" ]; then + # Remount /sys/fs/cgroup so the new cgroup namespace's view becomes the + # fs root; inner container cgroups end up under the envbox container's + # cgroup on the host. + umount /sys/fs/cgroup || { echo "envbox: failed to umount /sys/fs/cgroup" >&2; exit 1; } + mount -t cgroup2 cgroup /sys/fs/cgroup || { echo "envbox: failed to mount cgroup2 on /sys/fs/cgroup" >&2; exit 1; } + fi # move the processes from the root group to the /init group, # otherwise writing subtree_control fails with EBUSY. @@ -817,13 +823,15 @@ func TestWrapDockerdCmd(t *testing.T) { // - prepend the envbox_max_attempts value (so the embedded script can // reference it as a variable) // - guard the v2-only block on cgroup.controllers existing - // - perform the umount/mount inside that guard (cgroupv1 hosts unaffected) + // - only remount when /sys/fs/cgroup is still rooted at a nested path // - delegate via /init + subtree_control // - bound the retry loop against envbox_max_attempts // - exec into dockerd script := args[3] require.Contains(t, script, fmt.Sprintf("envbox_max_attempts=%d", cli.DockerdSubtreeControlMaxAttempts)) require.Contains(t, script, "[ -f /sys/fs/cgroup/cgroup.controllers ]") + require.Contains(t, script, `awk '$5 == "/sys/fs/cgroup" { print $4; exit }' /proc/self/mountinfo`) + require.Contains(t, script, `if [ "$cgroup_mount_root" != "/" ]; then`) require.Contains(t, script, "umount /sys/fs/cgroup") require.Contains(t, script, "mount -t cgroup2 cgroup /sys/fs/cgroup") require.Contains(t, script, "mkdir -p /sys/fs/cgroup/init") diff --git a/cli/wrap_dockerd.sh b/cli/wrap_dockerd.sh index 1ecfd5d..74984ab 100644 --- a/cli/wrap_dockerd.sh +++ b/cli/wrap_dockerd.sh @@ -5,11 +5,17 @@ # (https://github.com/moby/moby/blob/8d9e3502aba39127e4d12196dae16d306f76993d/hack/dind#L61-L79), # bounded by envbox_max_attempts. if [ -f /sys/fs/cgroup/cgroup.controllers ]; then - # Remount /sys/fs/cgroup so the new cgroup namespace's view becomes the - # fs root; inner container cgroups end up under the envbox container's - # cgroup on the host. - umount /sys/fs/cgroup || { echo "envbox: failed to umount /sys/fs/cgroup" >&2; exit 1; } - mount -t cgroup2 cgroup /sys/fs/cgroup || { echo "envbox: failed to mount cgroup2 on /sys/fs/cgroup" >&2; exit 1; } + # Some runtimes already root /sys/fs/cgroup at "/" after unsharing the + # cgroup namespace; remounting it again fails with EBUSY. Only remount + # when it's still rooted at a nested host path. + cgroup_mount_root=$(awk '$5 == "/sys/fs/cgroup" { print $4; exit }' /proc/self/mountinfo) + if [ "$cgroup_mount_root" != "/" ]; then + # Remount /sys/fs/cgroup so the new cgroup namespace's view becomes the + # fs root; inner container cgroups end up under the envbox container's + # cgroup on the host. + umount /sys/fs/cgroup || { echo "envbox: failed to umount /sys/fs/cgroup" >&2; exit 1; } + mount -t cgroup2 cgroup /sys/fs/cgroup || { echo "envbox: failed to mount cgroup2 on /sys/fs/cgroup" >&2; exit 1; } + fi # move the processes from the root group to the /init group, # otherwise writing subtree_control fails with EBUSY. From 2ce0cc7f9c964dbd560a0f4ce94d8c304fab45fb Mon Sep 17 00:00:00 2001 From: George Katsitadze Date: Mon, 20 Jul 2026 11:10:17 -0700 Subject: [PATCH 2/2] support stacked mounts when checking to remount /sys/fs/cgroup --- cli/docker_test.go | 11 +++++++++-- cli/wrap_dockerd.sh | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cli/docker_test.go b/cli/docker_test.go index d51b8ea..9074ce6 100644 --- a/cli/docker_test.go +++ b/cli/docker_test.go @@ -194,7 +194,13 @@ if [ -f /sys/fs/cgroup/cgroup.controllers ]; then # Some runtimes already root /sys/fs/cgroup at "/" after unsharing the # cgroup namespace; remounting it again fails with EBUSY. Only remount # when it's still rooted at a nested host path. - cgroup_mount_root=$(awk '$5 == "/sys/fs/cgroup" { print $4; exit }' /proc/self/mountinfo) + # When mounts are stacked at /sys/fs/cgroup, the visible mount is the + # one whose ID is not another same-location mount's parent (see + # proc_pid_mountinfo(5)). + cgroup_mount_root=$(awk ' + $5 == "/sys/fs/cgroup" { root[$1] = $4; isparent[$2] = 1 } + END { for (id in root) if (!(id in isparent)) print root[id] } + ' /proc/self/mountinfo) if [ "$cgroup_mount_root" != "/" ]; then # Remount /sys/fs/cgroup so the new cgroup namespace's view becomes the # fs root; inner container cgroups end up under the envbox container's @@ -830,7 +836,8 @@ func TestWrapDockerdCmd(t *testing.T) { script := args[3] require.Contains(t, script, fmt.Sprintf("envbox_max_attempts=%d", cli.DockerdSubtreeControlMaxAttempts)) require.Contains(t, script, "[ -f /sys/fs/cgroup/cgroup.controllers ]") - require.Contains(t, script, `awk '$5 == "/sys/fs/cgroup" { print $4; exit }' /proc/self/mountinfo`) + require.Contains(t, script, `$5 == "/sys/fs/cgroup" { root[$1] = $4; isparent[$2] = 1 }`) + require.Contains(t, script, `END { for (id in root) if (!(id in isparent)) print root[id] }`) require.Contains(t, script, `if [ "$cgroup_mount_root" != "/" ]; then`) require.Contains(t, script, "umount /sys/fs/cgroup") require.Contains(t, script, "mount -t cgroup2 cgroup /sys/fs/cgroup") diff --git a/cli/wrap_dockerd.sh b/cli/wrap_dockerd.sh index 74984ab..ac0af9a 100644 --- a/cli/wrap_dockerd.sh +++ b/cli/wrap_dockerd.sh @@ -8,7 +8,13 @@ if [ -f /sys/fs/cgroup/cgroup.controllers ]; then # Some runtimes already root /sys/fs/cgroup at "/" after unsharing the # cgroup namespace; remounting it again fails with EBUSY. Only remount # when it's still rooted at a nested host path. - cgroup_mount_root=$(awk '$5 == "/sys/fs/cgroup" { print $4; exit }' /proc/self/mountinfo) + # When mounts are stacked at /sys/fs/cgroup, the visible mount is the + # one whose ID is not another same-location mount's parent (see + # proc_pid_mountinfo(5)). + cgroup_mount_root=$(awk ' + $5 == "/sys/fs/cgroup" { root[$1] = $4; isparent[$2] = 1 } + END { for (id in root) if (!(id in isparent)) print root[id] } + ' /proc/self/mountinfo) if [ "$cgroup_mount_root" != "/" ]; then # Remount /sys/fs/cgroup so the new cgroup namespace's view becomes the # fs root; inner container cgroups end up under the envbox container's