From b8d03ce7759630a1ee274bf00de1af8e1c2c651e Mon Sep 17 00:00:00 2001 From: Ievgen Bondarenko Date: Tue, 19 May 2026 17:06:35 -0700 Subject: [PATCH] cgroupfs: drop registry entries on filesystem release Commit 26ef5174081be0b4b1f750a97e75ee6bad5d5a53 added CgroupRegistry.RemoveCgroup and called it from dir.RmDir so that destroyed cgroup directories do not stay in kernel.CgroupRegistry.cgroups across save/restore. That fix covers rmdir but two release paths still leak: 1. GetFilesystem error window. newCgroupInode at base.go:190 calls r.AddCgroup for the root cgroup before prepareInitialCgroup and r.Register run. On either failure the code calls rootD.DecRef and fs.VFSFilesystem().DecRef. Release then runs but currently skips ReleaseCgroupHierarchy and Unregister because fs.hierarchyID is still InvalidCgroupHierarchyID. The root cgroup id stays in the registry forever. Repeated mount failures accumulate unbounded entries. 2. prepareInitialCgroup creates intermediate cgroup directories via newDirWithOwner, each of which calls AddCgroup. If prepareInitialCgroup fails partway, the same residue applies. Walk fs.root in Release and call RemoveCgroup for each cgroupInode in the subtree. RemoveCgroup is documented as a no-op for ids not in the map (cgroup.go:667-669), so cgroups that RmDir already removed cost nothing and the rmdir path stays the primary owner. Signed-off-by: Ievgen Bondarenko --- pkg/sentry/fsimpl/cgroupfs/cgroupfs.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go b/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go index efc7d8d5027..61a4ee6a19d 100644 --- a/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go +++ b/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go @@ -468,6 +468,9 @@ func (fs *filesystem) Release(ctx context.Context) { k := kernel.KernelFromContext(ctx) r := k.CgroupRegistry() + // Remove registry entries for cgroups still owned by this filesystem. + fs.removeCgroupsFromRegistry(r) + if fs.hierarchyID != kernel.InvalidCgroupHierarchyID { k.ReleaseCgroupHierarchy(fs.hierarchyID) r.Unregister(ctx, fs.hierarchyID) @@ -481,6 +484,27 @@ func (fs *filesystem) Release(ctx context.Context) { fs.Filesystem.Release(ctx) } +// removeCgroupsFromRegistry removes each cgroup in fs.root from the registry. +func (fs *filesystem) removeCgroupsFromRegistry(r *kernel.CgroupRegistry) { + if fs.root == nil { + return + } + rootInode, ok := fs.root.Inode().(*cgroupInode) + if !ok { + return + } + var walk func(c *cgroupInode) + walk = func(c *cgroupInode) { + r.RemoveCgroup(c.id) + c.dir.forEachChildDir(func(child *dir) { + if child.cgi != nil { + walk(child.cgi) + } + }) + } + walk(rootInode) +} + // MountOptions implements vfs.FilesystemImpl.MountOptions. func (fs *filesystem) MountOptions() string { var cnames []string