From 4e262509b8bc584bf4a57afbb6c21e2e74cdd5e0 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 13 Oct 2025 15:54:45 -0700 Subject: [PATCH 1/2] libct: close child fds on prepareCgroupFD error The (*setns).start is supposed to close child fds once the child has started, or upon an error. Commit 5af4dd4e6 added a bug -- child fds are not closed if prepareCgroupFD fails. Fix by adding a missing call to closeChild. I'm not sure how to write a good test case for it. Found when working on PR 4928 (and tested in there). Fixes: 5af4dd4e6 Signed-off-by: Kir Kolyshkin --- libcontainer/process_linux.go | 1 + 1 file changed, 1 insertion(+) diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index c91bf5515ca..7d0a6b5dd97 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -357,6 +357,7 @@ func (p *setnsProcess) start() (retErr error) { fd, err := p.prepareCgroupFD() if err != nil { + p.comm.closeChild() return err } From 871052b791dfb4ade71329424fd0c683aeed4d21 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 14 Oct 2025 11:30:28 -0700 Subject: [PATCH 2/2] libct: refactor setnsProcess.start Factor startWithCgroupFD out of start to reduce the start complexity. This also implements a more future-proof way of calling p.comm.closeChild. Co-authored-by: lifubang Signed-off-by: Kir Kolyshkin --- libcontainer/process_linux.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index 7d0a6b5dd97..c1c27494fd6 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -352,22 +352,21 @@ func (p *setnsProcess) prepareCgroupFD() (*os.File, error) { return fd, nil } -func (p *setnsProcess) start() (retErr error) { - defer p.comm.closeParent() +// startWithCgroupFD starts a process via clone3 with CLONE_INTO_CGROUP, +// with a fallback if it fails (e.g. not available). +func (p *setnsProcess) startWithCgroupFD() error { + // Close the child side of the pipes. + defer p.comm.closeChild() fd, err := p.prepareCgroupFD() if err != nil { - p.comm.closeChild() return err } - - // Get the "before" value of oom kill count. - oom, _ := p.manager.OOMKillCount() - - err = p.startWithCPUAffinity() if fd != nil { - fd.Close() + defer fd.Close() } + + err = p.startWithCPUAffinity() if err != nil && p.cmd.SysProcAttr.UseCgroupFD { logrus.Debugf("exec with CLONE_INTO_CGROUP failed: %v; retrying without", err) // SysProcAttr.CgroupFD is never used when UseCgroupFD is unset. @@ -375,9 +374,16 @@ func (p *setnsProcess) start() (retErr error) { err = p.startWithCPUAffinity() } - // Close the child-side of the pipes (controlled by child). - p.comm.closeChild() - if err != nil { + return err +} + +func (p *setnsProcess) start() (retErr error) { + defer p.comm.closeParent() + + // Get the "before" value of oom kill count. + oom, _ := p.manager.OOMKillCount() + + if err := p.startWithCgroupFD(); err != nil { return fmt.Errorf("error starting setns process: %w", err) }